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         /*
249         if (typeof (tree.menu) != 'undefined') {
250             tree.menu.parentType = cn.xtype;
251             tree.menu.triggerEl = cn.el;
252             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
253             
254         }
255         */
256         if (!tree.items || !tree.items.length) {
257             cn.items = nitems;
258             return cn;
259         }
260         var items = tree.items;
261         delete tree.items;
262         
263         //Roo.log(items.length);
264             // add the items..
265         for(var i =0;i < items.length;i++) {
266             nitems.push(cn.addxtype(Roo.apply({}, items[i])));
267         }
268         
269         cn.items = nitems;
270         
271         return cn;
272     }
273     
274     
275     
276     
277 });
278
279  /*
280  * - LGPL
281  *
282  * Body
283  * 
284  */
285
286 /**
287  * @class Roo.bootstrap.Body
288  * @extends Roo.bootstrap.Component
289  * Bootstrap Body class
290  * 
291  * @constructor
292  * Create a new body
293  * @param {Object} config The config object
294  */
295
296 Roo.bootstrap.Body = function(config){
297     Roo.bootstrap.Body.superclass.constructor.call(this, config);
298     this.el = Roo.get(document.body);
299     if (this.cls && this.cls.length) {
300         Roo.get(document.body).addClass(this.cls);
301     }
302 };
303
304 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
305       
306         autoCreate : {
307         cls: 'container'
308     },
309     onRender : function(ct, position)
310     {
311        /* Roo.log("Roo.bootstrap.Body - onRender");
312         if (this.cls && this.cls.length) {
313             Roo.get(document.body).addClass(this.cls);
314         }
315         // style??? xttr???
316         */
317     }
318     
319     
320  
321    
322 });
323
324  /*
325  * - LGPL
326  *
327  * button group
328  * 
329  */
330
331
332 /**
333  * @class Roo.bootstrap.ButtonGroup
334  * @extends Roo.bootstrap.Component
335  * Bootstrap ButtonGroup class
336  * @cfg {String} size lg | sm | xs (default empty normal)
337  * @cfg {String} align vertical | justified  (default none)
338  * @cfg {String} direction up | down (default down)
339  * @cfg {Boolean} toolbar false | true
340  * @cfg {Boolean} btn true | false
341  * 
342  * 
343  * @constructor
344  * Create a new Input
345  * @param {Object} config The config object
346  */
347
348 Roo.bootstrap.ButtonGroup = function(config){
349     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
350 };
351
352 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
353     
354     size: '',
355     align: '',
356     direction: '',
357     toolbar: false,
358     btn: true,
359
360     getAutoCreate : function(){
361         var cfg = {
362             cls: 'btn-group',
363             html : null
364         }
365         
366         cfg.html = this.html || cfg.html;
367         
368         if (this.toolbar) {
369             cfg = {
370                 cls: 'btn-toolbar',
371                 html: null
372             }
373             
374             return cfg;
375         }
376         
377         if (['vertical','justified'].indexOf(this.align)!==-1) {
378             cfg.cls = 'btn-group-' + this.align;
379             
380             if (this.align == 'justified') {
381                 console.log(this.items);
382             }
383         }
384         
385         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
386             cfg.cls += ' btn-group-' + this.size;
387         }
388         
389         if (this.direction == 'up') {
390             cfg.cls += ' dropup' ;
391         }
392         
393         return cfg;
394     }
395    
396 });
397
398  /*
399  * - LGPL
400  *
401  * button
402  * 
403  */
404
405 /**
406  * @class Roo.bootstrap.Button
407  * @extends Roo.bootstrap.Component
408  * Bootstrap Button class
409  * @cfg {String} html The button content
410  * @cfg {String} weight default (or empty) | primary | success | info | warning | danger | link
411  * @cfg {String} size empty | lg | sm | xs
412  * @cfg {String} tag empty | a | input | submit
413  * @cfg {String} href empty or href
414  * @cfg {Boolean} disabled false | true
415  * @cfg {Boolean} isClose false | true
416  * @cfg {String} glyphicon empty | adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out
417  * @cfg {String} badge text for badge
418  * @cfg {String} theme default (or empty) | glow
419  * @cfg {Boolean} inverse false | true
420  * @cfg {Boolean} toggle false | true
421  * @cfg {String} ontext text for on toggle state
422  * @cfg {String} offtext text for off toggle state
423  * @cfg {Boolean} defaulton true | false
424  * @cfg {Boolean} preventDefault (true | false) default true
425  * @cfg {Boolean} removeClass true | false remove the standard class..
426  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
427  * 
428  * @constructor
429  * Create a new button
430  * @param {Object} config The config object
431  */
432
433
434 Roo.bootstrap.Button = function(config){
435     Roo.bootstrap.Button.superclass.constructor.call(this, config);
436     this.addEvents({
437         // raw events
438         /**
439          * @event click
440          * When a butotn is pressed
441          * @param {Roo.EventObject} e
442          */
443         "click" : true,
444          /**
445          * @event toggle
446          * After the button has been toggles
447          * @param {Roo.EventObject} e
448          * @param {boolean} pressed (also available as button.pressed)
449          */
450         "toggle" : true
451     });
452 };
453
454 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
455     html: false,
456     active: false,
457     weight: '',
458     size: '',
459     tag: 'button',
460     href: '',
461     disabled: false,
462     isClose: false,
463     glyphicon: '',
464     badge: '',
465     theme: 'default',
466     inverse: false,
467     
468     toggle: false,
469     ontext: 'ON',
470     offtext: 'OFF',
471     defaulton: true,
472     preventDefault: true,
473     removeClass: false,
474     name: false,
475     target: false,
476     
477     
478     pressed : null,
479      
480     
481     getAutoCreate : function(){
482         
483         var cfg = {
484             tag : 'button',
485             cls : 'roo-button',
486             html: ''
487         };
488         
489         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
490             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
491             this.tag = 'button';
492         } else {
493             cfg.tag = this.tag;
494         }
495         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
496         
497         if (this.toggle == true) {
498             cfg={
499                 tag: 'div',
500                 cls: 'slider-frame roo-button',
501                 cn: [
502                     {
503                         tag: 'span',
504                         'data-on-text':'ON',
505                         'data-off-text':'OFF',
506                         cls: 'slider-button',
507                         html: this.offtext
508                     }
509                 ]
510             };
511             
512             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
513                 cfg.cls += ' '+this.weight;
514             }
515             
516             return cfg;
517         }
518         
519         if (this.isClose) {
520             cfg.cls += ' close';
521             
522             cfg["aria-hidden"] = true;
523             
524             cfg.html = "&times;";
525             
526             return cfg;
527         }
528         
529          
530         if (this.theme==='default') {
531             cfg.cls = 'btn roo-button';
532             
533             //if (this.parentType != 'Navbar') {
534             this.weight = this.weight.length ?  this.weight : 'default';
535             //}
536             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
537                 
538                 cfg.cls += ' btn-' + this.weight;
539             }
540         } else if (this.theme==='glow') {
541             
542             cfg.tag = 'a';
543             cfg.cls = 'btn-glow roo-button';
544             
545             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
546                 
547                 cfg.cls += ' ' + this.weight;
548             }
549         }
550    
551         
552         if (this.inverse) {
553             this.cls += ' inverse';
554         }
555         
556         
557         if (this.active) {
558             cfg.cls += ' active';
559         }
560         
561         if (this.disabled) {
562             cfg.disabled = 'disabled';
563         }
564         
565         if (this.items) {
566             Roo.log('changing to ul' );
567             cfg.tag = 'ul';
568             this.glyphicon = 'caret';
569         }
570         
571         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
572          
573         //gsRoo.log(this.parentType);
574         if (this.parentType === 'Navbar' && !this.parent().bar) {
575             Roo.log('changing to li?');
576             
577             cfg.tag = 'li';
578             
579             cfg.cls = '';
580             cfg.cn =  [{
581                 tag : 'a',
582                 cls : 'roo-button',
583                 html : this.html,
584                 href : this.href || '#'
585             }];
586             if (this.menu) {
587                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
588                 cfg.cls += ' dropdown';
589             }   
590             
591             delete cfg.html;
592             
593         }
594         
595        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
596         
597         if (this.glyphicon) {
598             cfg.html = ' ' + cfg.html;
599             
600             cfg.cn = [
601                 {
602                     tag: 'span',
603                     cls: 'glyphicon glyphicon-' + this.glyphicon
604                 }
605             ];
606         }
607         
608         if (this.badge) {
609             cfg.html += ' ';
610             
611             cfg.tag = 'a';
612             
613 //            cfg.cls='btn roo-button';
614             
615             cfg.href=this.href;
616             
617             var value = cfg.html;
618             
619             if(this.glyphicon){
620                 value = {
621                             tag: 'span',
622                             cls: 'glyphicon glyphicon-' + this.glyphicon,
623                             html: this.html
624                         };
625                 
626             }
627             
628             cfg.cn = [
629                 value,
630                 {
631                     tag: 'span',
632                     cls: 'badge',
633                     html: this.badge
634                 }
635             ];
636             
637             cfg.html='';
638         }
639         
640         if (this.menu) {
641             cfg.cls += ' dropdown';
642             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
643         }
644         
645         if (cfg.tag !== 'a' && this.href !== '') {
646             throw "Tag must be a to set href.";
647         } else if (this.href.length > 0) {
648             cfg.href = this.href;
649         }
650         
651         if(this.removeClass){
652             cfg.cls = '';
653         }
654         
655         if(this.target){
656             cfg.target = this.target;
657         }
658         
659         return cfg;
660     },
661     initEvents: function() {
662        // Roo.log('init events?');
663 //        Roo.log(this.el.dom);
664         // add the menu...
665         
666         if (typeof (this.menu) != 'undefined') {
667             this.menu.parentType = this.xtype;
668             this.menu.triggerEl = this.el;
669             this.addxtype(Roo.apply({}, this.menu));
670         }
671
672
673        if (this.el.hasClass('roo-button')) {
674             this.el.on('click', this.onClick, this);
675        } else {
676             this.el.select('.roo-button').on('click', this.onClick, this);
677        }
678        
679        if(this.removeClass){
680            this.el.on('click', this.onClick, this);
681        }
682        
683        this.el.enableDisplayMode();
684         
685     },
686     onClick : function(e)
687     {
688         if (this.disabled) {
689             return;
690         }
691         
692         Roo.log('button on click ');
693         if(this.preventDefault){
694             e.preventDefault();
695         }
696         if (this.pressed === true || this.pressed === false) {
697             this.pressed = !this.pressed;
698             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
699             this.fireEvent('toggle', this, e, this.pressed);
700         }
701         
702         
703         this.fireEvent('click', this, e);
704     },
705     
706     /**
707      * Enables this button
708      */
709     enable : function()
710     {
711         this.disabled = false;
712         this.el.removeClass('disabled');
713     },
714     
715     /**
716      * Disable this button
717      */
718     disable : function()
719     {
720         this.disabled = true;
721         this.el.addClass('disabled');
722     },
723      /**
724      * sets the active state on/off, 
725      * @param {Boolean} state (optional) Force a particular state
726      */
727     setActive : function(v) {
728         
729         this.el[v ? 'addClass' : 'removeClass']('active');
730     },
731      /**
732      * toggles the current active state 
733      */
734     toggleActive : function()
735     {
736        var active = this.el.hasClass('active');
737        this.setActive(!active);
738        
739         
740     },
741     setText : function(str)
742     {
743         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
744     },
745     getText : function()
746     {
747         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
748     },
749     hide: function() {
750        
751      
752         this.el.hide();   
753     },
754     show: function() {
755        
756         this.el.show();   
757     }
758     
759     
760 });
761
762  /*
763  * - LGPL
764  *
765  * column
766  * 
767  */
768
769 /**
770  * @class Roo.bootstrap.Column
771  * @extends Roo.bootstrap.Component
772  * Bootstrap Column class
773  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
774  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
775  * @cfg {Number} md colspan out of 12 for computer-sized screens
776  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
777  * @cfg {String} html content of column.
778  * 
779  * @constructor
780  * Create a new Column
781  * @param {Object} config The config object
782  */
783
784 Roo.bootstrap.Column = function(config){
785     Roo.bootstrap.Column.superclass.constructor.call(this, config);
786 };
787
788 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
789     
790     xs: null,
791     sm: null,
792     md: null,
793     lg: null,
794     html: '',
795     offset: 0,
796     
797     getAutoCreate : function(){
798         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
799         
800         cfg = {
801             tag: 'div',
802             cls: 'column'
803         };
804         
805         var settings=this;
806         ['xs','sm','md','lg'].map(function(size){
807             if (settings[size]) {
808                 cfg.cls += ' col-' + size + '-' + settings[size];
809             }
810         });
811         if (this.html.length) {
812             cfg.html = this.html;
813         }
814         
815         return cfg;
816     }
817    
818 });
819
820  
821
822  /*
823  * - LGPL
824  *
825  * page container.
826  * 
827  */
828
829
830 /**
831  * @class Roo.bootstrap.Container
832  * @extends Roo.bootstrap.Component
833  * Bootstrap Container class
834  * @cfg {Boolean} jumbotron is it a jumbotron element
835  * @cfg {String} html content of element
836  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
837  * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
838  * @cfg {String} header content of header (for panel)
839  * @cfg {String} footer content of footer (for panel)
840  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
841  * @cfg {String} tag (header|aside|section) type of HTML tag.
842
843  *     
844  * @constructor
845  * Create a new Container
846  * @param {Object} config The config object
847  */
848
849 Roo.bootstrap.Container = function(config){
850     Roo.bootstrap.Container.superclass.constructor.call(this, config);
851 };
852
853 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
854     
855     jumbotron : false,
856     well: '',
857     panel : '',
858     header: '',
859     footer : '',
860     sticky: '',
861     tag : false,
862   
863      
864     getChildContainer : function() {
865         
866         if(!this.el){
867             return false;
868         }
869         
870         if (this.panel.length) {
871             return this.el.select('.panel-body',true).first();
872         }
873         
874         return this.el;
875     },
876     
877     
878     getAutoCreate : function(){
879         
880         var cfg = {
881             tag : this.tag || 'div',
882             html : '',
883             cls : ''
884         };
885         if (this.jumbotron) {
886             cfg.cls = 'jumbotron';
887         }
888         // - this is applied by the parent..
889         //if (this.cls) {
890         //    cfg.cls = this.cls + '';
891         //}
892         
893         if (this.sticky.length) {
894             
895             var bd = Roo.get(document.body);
896             if (!bd.hasClass('bootstrap-sticky')) {
897                 bd.addClass('bootstrap-sticky');
898                 Roo.select('html',true).setStyle('height', '100%');
899             }
900              
901             cfg.cls += 'bootstrap-sticky-' + this.sticky;
902         }
903         
904         
905         if (this.well.length) {
906             switch (this.well) {
907                 case 'lg':
908                 case 'sm':
909                     cfg.cls +=' well well-' +this.well;
910                     break;
911                 default:
912                     cfg.cls +=' well';
913                     break;
914             }
915         }
916         
917         var body = cfg;
918         
919         if (this.panel.length) {
920             cfg.cls += ' panel panel-' + this.panel;
921             cfg.cn = [];
922             if (this.header.length) {
923                 cfg.cn.push({
924                     
925                     cls : 'panel-heading',
926                     cn : [{
927                         tag: 'h3',
928                         cls : 'panel-title',
929                         html : this.header
930                     }]
931                     
932                 });
933             }
934             body = false;
935             cfg.cn.push({
936                 cls : 'panel-body',
937                 html : this.html
938             });
939             
940             
941             if (this.footer.length) {
942                 cfg.cn.push({
943                     cls : 'panel-footer',
944                     html : this.footer
945                     
946                 });
947             }
948             
949         }
950         
951         if (body) {
952             body.html = this.html || cfg.html;
953         }
954         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
955             cfg.cls =  'container';
956         }
957         
958         return cfg;
959     }
960    
961 });
962
963  /*
964  * - LGPL
965  *
966  * image
967  * 
968  */
969
970
971 /**
972  * @class Roo.bootstrap.Img
973  * @extends Roo.bootstrap.Component
974  * Bootstrap Img class
975  * @cfg {Boolean} imgResponsive false | true
976  * @cfg {String} border rounded | circle | thumbnail
977  * @cfg {String} src image source
978  * @cfg {String} alt image alternative text
979  * @cfg {String} href a tag href
980  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
981  * 
982  * @constructor
983  * Create a new Input
984  * @param {Object} config The config object
985  */
986
987 Roo.bootstrap.Img = function(config){
988     Roo.bootstrap.Img.superclass.constructor.call(this, config);
989     
990     this.addEvents({
991         // img events
992         /**
993          * @event click
994          * The img click event for the img.
995          * @param {Roo.EventObject} e
996          */
997         "click" : true
998     });
999 };
1000
1001 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1002     
1003     imgResponsive: true,
1004     border: '',
1005     src: '',
1006     href: false,
1007     target: false,
1008
1009     getAutoCreate : function(){
1010         
1011         var cfg = {
1012             tag: 'img',
1013             cls: (this.imgResponsive) ? 'img-responsive' : '',
1014             html : null
1015         }
1016         
1017         cfg.html = this.html || cfg.html;
1018         
1019         cfg.src = this.src || cfg.src;
1020         
1021         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1022             cfg.cls += ' img-' + this.border;
1023         }
1024         
1025         if(this.alt){
1026             cfg.alt = this.alt;
1027         }
1028         
1029         if(this.href){
1030             var a = {
1031                 tag: 'a',
1032                 href: this.href,
1033                 cn: [
1034                     cfg
1035                 ]
1036             }
1037             
1038             if(this.target){
1039                 a.target = this.target;
1040             }
1041             
1042         }
1043         
1044         
1045         return (this.href) ? a : cfg;
1046     },
1047     
1048     initEvents: function() {
1049         
1050         if(!this.href){
1051             this.el.on('click', this.onClick, this);
1052         }
1053     },
1054     
1055     onClick : function(e)
1056     {
1057         Roo.log('img onclick');
1058         this.fireEvent('click', this, e);
1059     }
1060    
1061 });
1062
1063  /*
1064  * - LGPL
1065  *
1066  * image
1067  * 
1068  */
1069
1070
1071 /**
1072  * @class Roo.bootstrap.Link
1073  * @extends Roo.bootstrap.Component
1074  * Bootstrap Link Class
1075  * @cfg {String} alt image alternative text
1076  * @cfg {String} href a tag href
1077  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1078  * @cfg {String} html the content of the link.
1079
1080  * 
1081  * @constructor
1082  * Create a new Input
1083  * @param {Object} config The config object
1084  */
1085
1086 Roo.bootstrap.Link = function(config){
1087     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1088     
1089     this.addEvents({
1090         // img events
1091         /**
1092          * @event click
1093          * The img click event for the img.
1094          * @param {Roo.EventObject} e
1095          */
1096         "click" : true
1097     });
1098 };
1099
1100 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1101     
1102     href: false,
1103     target: false,
1104
1105     getAutoCreate : function(){
1106         
1107         var cfg = {
1108             tag: 'a',
1109             html : this.html || 'html-missing'
1110         }
1111         
1112         
1113         if(this.alt){
1114             cfg.alt = this.alt;
1115         }
1116         cfg.href = this.href || '#';
1117         if(this.target){
1118             cfg.target = this.target;
1119         }
1120         
1121         return cfg;
1122     },
1123     
1124     initEvents: function() {
1125         
1126         if(!this.href){
1127             this.el.on('click', this.onClick, this);
1128         }
1129     },
1130     
1131     onClick : function(e)
1132     {
1133         //Roo.log('img onclick');
1134         this.fireEvent('click', this, e);
1135     }
1136    
1137 });
1138
1139  /*
1140  * - LGPL
1141  *
1142  * header
1143  * 
1144  */
1145
1146 /**
1147  * @class Roo.bootstrap.Header
1148  * @extends Roo.bootstrap.Component
1149  * Bootstrap Header class
1150  * @cfg {String} html content of header
1151  * @cfg {Number} level (1|2|3|4|5|6) default 1
1152  * 
1153  * @constructor
1154  * Create a new Header
1155  * @param {Object} config The config object
1156  */
1157
1158
1159 Roo.bootstrap.Header  = function(config){
1160     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1161 };
1162
1163 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1164     
1165     //href : false,
1166     html : false,
1167     level : 1,
1168     
1169     
1170     
1171     getAutoCreate : function(){
1172         
1173         var cfg = {
1174             tag: 'h' + (1 *this.level),
1175             html: this.html || 'fill in html'
1176         } ;
1177         
1178         return cfg;
1179     }
1180    
1181 });
1182
1183  
1184
1185  /*
1186  * Based on:
1187  * Ext JS Library 1.1.1
1188  * Copyright(c) 2006-2007, Ext JS, LLC.
1189  *
1190  * Originally Released Under LGPL - original licence link has changed is not relivant.
1191  *
1192  * Fork - LGPL
1193  * <script type="text/javascript">
1194  */
1195  
1196 /**
1197  * @class Roo.bootstrap.MenuMgr
1198  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1199  * @singleton
1200  */
1201 Roo.bootstrap.MenuMgr = function(){
1202    var menus, active, groups = {}, attached = false, lastShow = new Date();
1203
1204    // private - called when first menu is created
1205    function init(){
1206        menus = {};
1207        active = new Roo.util.MixedCollection();
1208        Roo.get(document).addKeyListener(27, function(){
1209            if(active.length > 0){
1210                hideAll();
1211            }
1212        });
1213    }
1214
1215    // private
1216    function hideAll(){
1217        if(active && active.length > 0){
1218            var c = active.clone();
1219            c.each(function(m){
1220                m.hide();
1221            });
1222        }
1223    }
1224
1225    // private
1226    function onHide(m){
1227        active.remove(m);
1228        if(active.length < 1){
1229            Roo.get(document).un("mouseup", onMouseDown);
1230             
1231            attached = false;
1232        }
1233    }
1234
1235    // private
1236    function onShow(m){
1237        var last = active.last();
1238        lastShow = new Date();
1239        active.add(m);
1240        if(!attached){
1241           Roo.get(document).on("mouseup", onMouseDown);
1242            
1243            attached = true;
1244        }
1245        if(m.parentMenu){
1246           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1247           m.parentMenu.activeChild = m;
1248        }else if(last && last.isVisible()){
1249           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1250        }
1251    }
1252
1253    // private
1254    function onBeforeHide(m){
1255        if(m.activeChild){
1256            m.activeChild.hide();
1257        }
1258        if(m.autoHideTimer){
1259            clearTimeout(m.autoHideTimer);
1260            delete m.autoHideTimer;
1261        }
1262    }
1263
1264    // private
1265    function onBeforeShow(m){
1266        var pm = m.parentMenu;
1267        if(!pm && !m.allowOtherMenus){
1268            hideAll();
1269        }else if(pm && pm.activeChild && active != m){
1270            pm.activeChild.hide();
1271        }
1272    }
1273
1274    // private
1275    function onMouseDown(e){
1276         Roo.log("on MouseDown");
1277         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
1278            hideAll();
1279         }
1280         
1281         
1282    }
1283
1284    // private
1285    function onBeforeCheck(mi, state){
1286        if(state){
1287            var g = groups[mi.group];
1288            for(var i = 0, l = g.length; i < l; i++){
1289                if(g[i] != mi){
1290                    g[i].setChecked(false);
1291                }
1292            }
1293        }
1294    }
1295
1296    return {
1297
1298        /**
1299         * Hides all menus that are currently visible
1300         */
1301        hideAll : function(){
1302             hideAll();  
1303        },
1304
1305        // private
1306        register : function(menu){
1307            if(!menus){
1308                init();
1309            }
1310            menus[menu.id] = menu;
1311            menu.on("beforehide", onBeforeHide);
1312            menu.on("hide", onHide);
1313            menu.on("beforeshow", onBeforeShow);
1314            menu.on("show", onShow);
1315            var g = menu.group;
1316            if(g && menu.events["checkchange"]){
1317                if(!groups[g]){
1318                    groups[g] = [];
1319                }
1320                groups[g].push(menu);
1321                menu.on("checkchange", onCheck);
1322            }
1323        },
1324
1325         /**
1326          * Returns a {@link Roo.menu.Menu} object
1327          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1328          * be used to generate and return a new Menu instance.
1329          */
1330        get : function(menu){
1331            if(typeof menu == "string"){ // menu id
1332                return menus[menu];
1333            }else if(menu.events){  // menu instance
1334                return menu;
1335            }
1336            /*else if(typeof menu.length == 'number'){ // array of menu items?
1337                return new Roo.bootstrap.Menu({items:menu});
1338            }else{ // otherwise, must be a config
1339                return new Roo.bootstrap.Menu(menu);
1340            }
1341            */
1342            return false;
1343        },
1344
1345        // private
1346        unregister : function(menu){
1347            delete menus[menu.id];
1348            menu.un("beforehide", onBeforeHide);
1349            menu.un("hide", onHide);
1350            menu.un("beforeshow", onBeforeShow);
1351            menu.un("show", onShow);
1352            var g = menu.group;
1353            if(g && menu.events["checkchange"]){
1354                groups[g].remove(menu);
1355                menu.un("checkchange", onCheck);
1356            }
1357        },
1358
1359        // private
1360        registerCheckable : function(menuItem){
1361            var g = menuItem.group;
1362            if(g){
1363                if(!groups[g]){
1364                    groups[g] = [];
1365                }
1366                groups[g].push(menuItem);
1367                menuItem.on("beforecheckchange", onBeforeCheck);
1368            }
1369        },
1370
1371        // private
1372        unregisterCheckable : function(menuItem){
1373            var g = menuItem.group;
1374            if(g){
1375                groups[g].remove(menuItem);
1376                menuItem.un("beforecheckchange", onBeforeCheck);
1377            }
1378        }
1379    };
1380 }();/*
1381  * - LGPL
1382  *
1383  * menu
1384  * 
1385  */
1386
1387 /**
1388  * @class Roo.bootstrap.Menu
1389  * @extends Roo.bootstrap.Component
1390  * Bootstrap Menu class - container for MenuItems
1391  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1392  * 
1393  * @constructor
1394  * Create a new Menu
1395  * @param {Object} config The config object
1396  */
1397
1398
1399 Roo.bootstrap.Menu = function(config){
1400     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1401     if (this.registerMenu) {
1402         Roo.bootstrap.MenuMgr.register(this);
1403     }
1404     this.addEvents({
1405         /**
1406          * @event beforeshow
1407          * Fires before this menu is displayed
1408          * @param {Roo.menu.Menu} this
1409          */
1410         beforeshow : true,
1411         /**
1412          * @event beforehide
1413          * Fires before this menu is hidden
1414          * @param {Roo.menu.Menu} this
1415          */
1416         beforehide : true,
1417         /**
1418          * @event show
1419          * Fires after this menu is displayed
1420          * @param {Roo.menu.Menu} this
1421          */
1422         show : true,
1423         /**
1424          * @event hide
1425          * Fires after this menu is hidden
1426          * @param {Roo.menu.Menu} this
1427          */
1428         hide : true,
1429         /**
1430          * @event click
1431          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1432          * @param {Roo.menu.Menu} this
1433          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1434          * @param {Roo.EventObject} e
1435          */
1436         click : true,
1437         /**
1438          * @event mouseover
1439          * Fires when the mouse is hovering over this menu
1440          * @param {Roo.menu.Menu} this
1441          * @param {Roo.EventObject} e
1442          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1443          */
1444         mouseover : true,
1445         /**
1446          * @event mouseout
1447          * Fires when the mouse exits this menu
1448          * @param {Roo.menu.Menu} this
1449          * @param {Roo.EventObject} e
1450          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1451          */
1452         mouseout : true,
1453         /**
1454          * @event itemclick
1455          * Fires when a menu item contained in this menu is clicked
1456          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1457          * @param {Roo.EventObject} e
1458          */
1459         itemclick: true
1460     });
1461     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1462 };
1463
1464 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1465     
1466    /// html : false,
1467     //align : '',
1468     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1469     type: false,
1470     /**
1471      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1472      */
1473     registerMenu : true,
1474     
1475     menuItems :false, // stores the menu items..
1476     
1477     hidden:true,
1478     
1479     parentMenu : false,
1480     
1481     getChildContainer : function() {
1482         return this.el;  
1483     },
1484     
1485     getAutoCreate : function(){
1486          
1487         //if (['right'].indexOf(this.align)!==-1) {
1488         //    cfg.cn[1].cls += ' pull-right'
1489         //}
1490         
1491         
1492         var cfg = {
1493             tag : 'ul',
1494             cls : 'dropdown-menu' ,
1495             style : 'z-index:1000'
1496             
1497         }
1498         
1499         if (this.type === 'submenu') {
1500             cfg.cls = 'submenu active';
1501         }
1502         if (this.type === 'treeview') {
1503             cfg.cls = 'treeview-menu';
1504         }
1505         
1506         return cfg;
1507     },
1508     initEvents : function() {
1509         
1510        // Roo.log("ADD event");
1511        // Roo.log(this.triggerEl.dom);
1512         this.triggerEl.on('click', this.onTriggerPress, this);
1513         this.triggerEl.addClass('dropdown-toggle');
1514         this.el.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
1515
1516         this.el.on("mouseover", this.onMouseOver, this);
1517         this.el.on("mouseout", this.onMouseOut, this);
1518         
1519         
1520     },
1521     findTargetItem : function(e){
1522         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
1523         if(!t){
1524             return false;
1525         }
1526         //Roo.log(t);         Roo.log(t.id);
1527         if(t && t.id){
1528             //Roo.log(this.menuitems);
1529             return this.menuitems.get(t.id);
1530             
1531             //return this.items.get(t.menuItemId);
1532         }
1533         
1534         return false;
1535     },
1536     onClick : function(e){
1537         Roo.log("menu.onClick");
1538         var t = this.findTargetItem(e);
1539         if(!t){
1540             return;
1541         }
1542         Roo.log(e);
1543         /*
1544         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
1545             if(t == this.activeItem && t.shouldDeactivate(e)){
1546                 this.activeItem.deactivate();
1547                 delete this.activeItem;
1548                 return;
1549             }
1550             if(t.canActivate){
1551                 this.setActiveItem(t, true);
1552             }
1553             return;
1554             
1555             
1556         }
1557         */
1558         Roo.log('pass click event');
1559         
1560         t.onClick(e);
1561         
1562         this.fireEvent("click", this, t, e);
1563         
1564         this.hide();
1565     },
1566      onMouseOver : function(e){
1567         var t  = this.findTargetItem(e);
1568         //Roo.log(t);
1569         //if(t){
1570         //    if(t.canActivate && !t.disabled){
1571         //        this.setActiveItem(t, true);
1572         //    }
1573         //}
1574         
1575         this.fireEvent("mouseover", this, e, t);
1576     },
1577     isVisible : function(){
1578         return !this.hidden;
1579     },
1580      onMouseOut : function(e){
1581         var t  = this.findTargetItem(e);
1582         
1583         //if(t ){
1584         //    if(t == this.activeItem && t.shouldDeactivate(e)){
1585         //        this.activeItem.deactivate();
1586         //        delete this.activeItem;
1587         //    }
1588         //}
1589         this.fireEvent("mouseout", this, e, t);
1590     },
1591     
1592     
1593     /**
1594      * Displays this menu relative to another element
1595      * @param {String/HTMLElement/Roo.Element} element The element to align to
1596      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
1597      * the element (defaults to this.defaultAlign)
1598      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1599      */
1600     show : function(el, pos, parentMenu){
1601         this.parentMenu = parentMenu;
1602         if(!this.el){
1603             this.render();
1604         }
1605         this.fireEvent("beforeshow", this);
1606         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
1607     },
1608      /**
1609      * Displays this menu at a specific xy position
1610      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
1611      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1612      */
1613     showAt : function(xy, parentMenu, /* private: */_e){
1614         this.parentMenu = parentMenu;
1615         if(!this.el){
1616             this.render();
1617         }
1618         if(_e !== false){
1619             this.fireEvent("beforeshow", this);
1620             
1621             //xy = this.el.adjustForConstraints(xy);
1622         }
1623         //this.el.setXY(xy);
1624         //this.el.show();
1625         this.hideMenuItems();
1626         this.hidden = false;
1627         this.triggerEl.addClass('open');
1628         this.focus();
1629         this.fireEvent("show", this);
1630     },
1631     
1632     focus : function(){
1633         return;
1634         if(!this.hidden){
1635             this.doFocus.defer(50, this);
1636         }
1637     },
1638
1639     doFocus : function(){
1640         if(!this.hidden){
1641             this.focusEl.focus();
1642         }
1643     },
1644
1645     /**
1646      * Hides this menu and optionally all parent menus
1647      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
1648      */
1649     hide : function(deep){
1650         
1651         this.hideMenuItems();
1652         if(this.el && this.isVisible()){
1653             this.fireEvent("beforehide", this);
1654             if(this.activeItem){
1655                 this.activeItem.deactivate();
1656                 this.activeItem = null;
1657             }
1658             this.triggerEl.removeClass('open');;
1659             this.hidden = true;
1660             this.fireEvent("hide", this);
1661         }
1662         if(deep === true && this.parentMenu){
1663             this.parentMenu.hide(true);
1664         }
1665     },
1666     
1667     onTriggerPress  : function(e)
1668     {
1669         
1670         Roo.log('trigger press');
1671         //Roo.log(e.getTarget());
1672        // Roo.log(this.triggerEl.dom);
1673         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
1674             return;
1675         }
1676         if (this.isVisible()) {
1677             Roo.log('hide');
1678             this.hide();
1679         } else {
1680             this.show(this.triggerEl, false, false);
1681         }
1682         
1683         
1684     },
1685     
1686          
1687        
1688     
1689     hideMenuItems : function()
1690     {
1691         //$(backdrop).remove()
1692         Roo.select('.open',true).each(function(aa) {
1693             
1694             aa.removeClass('open');
1695           //var parent = getParent($(this))
1696           //var relatedTarget = { relatedTarget: this }
1697           
1698            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
1699           //if (e.isDefaultPrevented()) return
1700            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
1701         })
1702     },
1703     addxtypeChild : function (tree, cntr) {
1704         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
1705           
1706         this.menuitems.add(comp);
1707         return comp;
1708
1709     },
1710     getEl : function()
1711     {
1712         Roo.log(this.el);
1713         return this.el;
1714     }
1715 });
1716
1717  
1718  /*
1719  * - LGPL
1720  *
1721  * menu item
1722  * 
1723  */
1724
1725
1726 /**
1727  * @class Roo.bootstrap.MenuItem
1728  * @extends Roo.bootstrap.Component
1729  * Bootstrap MenuItem class
1730  * @cfg {String} html the menu label
1731  * @cfg {String} href the link
1732  * @cfg {Boolean} preventDefault (true | false) default true
1733  * 
1734  * 
1735  * @constructor
1736  * Create a new MenuItem
1737  * @param {Object} config The config object
1738  */
1739
1740
1741 Roo.bootstrap.MenuItem = function(config){
1742     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
1743     this.addEvents({
1744         // raw events
1745         /**
1746          * @event click
1747          * The raw click event for the entire grid.
1748          * @param {Roo.EventObject} e
1749          */
1750         "click" : true
1751     });
1752 };
1753
1754 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
1755     
1756     href : false,
1757     html : false,
1758     preventDefault: true,
1759     
1760     getAutoCreate : function(){
1761         var cfg= {
1762             tag: 'li',
1763             cls: 'dropdown-menu-item',
1764             cn: [
1765                     {
1766                         tag : 'a',
1767                         href : '#',
1768                         html : 'Link'
1769                     }
1770                 ]
1771         };
1772         if (this.parent().type == 'treeview') {
1773             cfg.cls = 'treeview-menu';
1774         }
1775         
1776         cfg.cn[0].href = this.href || cfg.cn[0].href ;
1777         cfg.cn[0].html = this.html || cfg.cn[0].html ;
1778         return cfg;
1779     },
1780     
1781     initEvents: function() {
1782         
1783         //this.el.select('a').on('click', this.onClick, this);
1784         
1785     },
1786     onClick : function(e)
1787     {
1788         Roo.log('item on click ');
1789         //if(this.preventDefault){
1790         //    e.preventDefault();
1791         //}
1792         //this.parent().hideMenuItems();
1793         
1794         this.fireEvent('click', this, e);
1795     },
1796     getEl : function()
1797     {
1798         return this.el;
1799     }
1800 });
1801
1802  
1803
1804  /*
1805  * - LGPL
1806  *
1807  * menu separator
1808  * 
1809  */
1810
1811
1812 /**
1813  * @class Roo.bootstrap.MenuSeparator
1814  * @extends Roo.bootstrap.Component
1815  * Bootstrap MenuSeparator class
1816  * 
1817  * @constructor
1818  * Create a new MenuItem
1819  * @param {Object} config The config object
1820  */
1821
1822
1823 Roo.bootstrap.MenuSeparator = function(config){
1824     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
1825 };
1826
1827 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
1828     
1829     getAutoCreate : function(){
1830         var cfg = {
1831             cls: 'divider',
1832             tag : 'li'
1833         };
1834         
1835         return cfg;
1836     }
1837    
1838 });
1839
1840  
1841
1842  
1843 /*
1844 <div class="modal fade">
1845   <div class="modal-dialog">
1846     <div class="modal-content">
1847       <div class="modal-header">
1848         <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
1849         <h4 class="modal-title">Modal title</h4>
1850       </div>
1851       <div class="modal-body">
1852         <p>One fine body&hellip;</p>
1853       </div>
1854       <div class="modal-footer">
1855         <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
1856         <button type="button" class="btn btn-primary">Save changes</button>
1857       </div>
1858     </div><!-- /.modal-content -->
1859   </div><!-- /.modal-dialog -->
1860 </div><!-- /.modal -->
1861 */
1862 /*
1863  * - LGPL
1864  *
1865  * page contgainer.
1866  * 
1867  */
1868
1869 /**
1870  * @class Roo.bootstrap.Modal
1871  * @extends Roo.bootstrap.Component
1872  * Bootstrap Modal class
1873  * @cfg {String} title Title of dialog
1874  * @cfg {Boolean} specificTitle (true|false) default false
1875  * @cfg {Array} buttons Array of buttons or standard button set..
1876  * @cfg {String} buttonPosition (left|right|center) default right
1877  * 
1878  * @constructor
1879  * Create a new Modal Dialog
1880  * @param {Object} config The config object
1881  */
1882
1883 Roo.bootstrap.Modal = function(config){
1884     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
1885     this.addEvents({
1886         // raw events
1887         /**
1888          * @event btnclick
1889          * The raw btnclick event for the button
1890          * @param {Roo.EventObject} e
1891          */
1892         "btnclick" : true
1893     });
1894     this.buttons = this.buttons || [];
1895 };
1896
1897 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
1898     
1899     title : 'test dialog',
1900    
1901     buttons : false,
1902     
1903     // set on load...
1904     body:  false,
1905     
1906     specificTitle: false,
1907     
1908     buttonPosition: 'right',
1909     
1910     onRender : function(ct, position)
1911     {
1912         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
1913      
1914         if(!this.el){
1915             var cfg = Roo.apply({},  this.getAutoCreate());
1916             cfg.id = Roo.id();
1917             //if(!cfg.name){
1918             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
1919             //}
1920             //if (!cfg.name.length) {
1921             //    delete cfg.name;
1922            // }
1923             if (this.cls) {
1924                 cfg.cls += ' ' + this.cls;
1925             }
1926             if (this.style) {
1927                 cfg.style = this.style;
1928             }
1929             this.el = Roo.get(document.body).createChild(cfg, position);
1930         }
1931         //var type = this.el.dom.type;
1932         
1933         if(this.tabIndex !== undefined){
1934             this.el.dom.setAttribute('tabIndex', this.tabIndex);
1935         }
1936         
1937         
1938         
1939         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
1940         this.maskEl.enableDisplayMode("block");
1941         this.maskEl.hide();
1942         //this.el.addClass("x-dlg-modal");
1943     
1944         if (this.buttons.length) {
1945             Roo.each(this.buttons, function(bb) {
1946                 b = Roo.apply({}, bb);
1947                 b.xns = b.xns || Roo.bootstrap;
1948                 b.xtype = b.xtype || 'Button';
1949                 if (typeof(b.listeners) == 'undefined') {
1950                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
1951                 }
1952                 
1953                 var btn = Roo.factory(b);
1954                 
1955                 btn.onRender(this.el.select('.modal-footer div').first());
1956                 
1957             },this);
1958         }
1959         // render the children.
1960         var nitems = [];
1961         
1962         if(typeof(this.items) != 'undefined'){
1963             var items = this.items;
1964             delete this.items;
1965
1966             for(var i =0;i < items.length;i++) {
1967                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
1968             }
1969         }
1970         
1971         this.items = nitems;
1972         
1973         this.body = this.el.select('.modal-body',true).first();
1974         this.close = this.el.select('.modal-header .close', true).first();
1975         this.footer = this.el.select('.modal-footer',true).first();
1976         this.initEvents();
1977         //this.el.addClass([this.fieldClass, this.cls]);
1978         
1979     },
1980     getAutoCreate : function(){
1981         
1982         
1983         var bdy = {
1984                 cls : 'modal-body',
1985                 html : this.html || ''
1986         };
1987         
1988         var title = {
1989             tag: 'h4',
1990             cls : 'modal-title',
1991             html : this.title
1992         };
1993         
1994         if(this.specificTitle){
1995             title = this.title;
1996         };
1997         
1998         return modal = {
1999             cls: "modal fade",
2000             style : 'display: none',
2001             cn : [
2002                 {
2003                     cls: "modal-dialog",
2004                     cn : [
2005                         {
2006                             cls : "modal-content",
2007                             cn : [
2008                                 {
2009                                     cls : 'modal-header',
2010                                     cn : [
2011                                         {
2012                                             tag: 'button',
2013                                             cls : 'close',
2014                                             html : '&times'
2015                                         },
2016                                         title
2017                                     ]
2018                                 },
2019                                 bdy,
2020                                 {
2021                                     cls : 'modal-footer',
2022                                     cn : [
2023                                         {
2024                                             tag: 'div',
2025                                             cls: 'btn-' + this.buttonPosition
2026                                         }
2027                                     ]
2028                                     
2029                                 }
2030                                 
2031                                 
2032                             ]
2033                             
2034                         }
2035                     ]
2036                         
2037                 }
2038             ]
2039             
2040             
2041         };
2042           
2043     },
2044     getChildContainer : function() {
2045          
2046          return this.el.select('.modal-body',true).first();
2047         
2048     },
2049     getButtonContainer : function() {
2050          return this.el.select('.modal-footer div',true).first();
2051         
2052     },
2053     initEvents : function()
2054     {
2055         this.el.select('.modal-header .close').on('click', this.hide, this);
2056 //        
2057 //        this.addxtype(this);
2058     },
2059     show : function() {
2060         
2061         if (!this.rendered) {
2062             this.render();
2063         }
2064        
2065         this.el.addClass('on');
2066         this.el.removeClass('fade');
2067         this.el.setStyle('display', 'block');
2068         Roo.get(document.body).addClass("x-body-masked");
2069         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2070         this.maskEl.show();
2071         this.el.setStyle('zIndex', '10001');
2072         this.fireEvent('show', this);
2073         
2074         
2075     },
2076     hide : function()
2077     {
2078         Roo.log('Modal hide?!');
2079         this.maskEl.hide();
2080         Roo.get(document.body).removeClass("x-body-masked");
2081         this.el.removeClass('on');
2082         this.el.addClass('fade');
2083         this.el.setStyle('display', 'none');
2084         this.fireEvent('hide', this);
2085     },
2086     
2087     addButton : function(str, cb)
2088     {
2089          
2090         
2091         var b = Roo.apply({}, { html : str } );
2092         b.xns = b.xns || Roo.bootstrap;
2093         b.xtype = b.xtype || 'Button';
2094         if (typeof(b.listeners) == 'undefined') {
2095             b.listeners = { click : cb.createDelegate(this)  };
2096         }
2097         
2098         var btn = Roo.factory(b);
2099            
2100         btn.onRender(this.el.select('.modal-footer div').first());
2101         
2102         return btn;   
2103        
2104     },
2105     
2106     setDefaultButton : function(btn)
2107     {
2108         //this.el.select('.modal-footer').()
2109     },
2110     resizeTo: function(w,h)
2111     {
2112         // skip..
2113     },
2114     setContentSize  : function(w, h)
2115     {
2116         
2117     },
2118     onButtonClick: function(btn,e)
2119     {
2120         //Roo.log([a,b,c]);
2121         this.fireEvent('btnclick', btn.name, e);
2122     },
2123     setTitle: function(str) {
2124         this.el.select('.modal-title',true).first().dom.innerHTML = str;
2125         
2126     }
2127 });
2128
2129
2130 Roo.apply(Roo.bootstrap.Modal,  {
2131     /**
2132          * Button config that displays a single OK button
2133          * @type Object
2134          */
2135         OK :  [{
2136             name : 'ok',
2137             weight : 'primary',
2138             html : 'OK'
2139         }], 
2140         /**
2141          * Button config that displays Yes and No buttons
2142          * @type Object
2143          */
2144         YESNO : [
2145             {
2146                 name  : 'no',
2147                 html : 'No'
2148             },
2149             {
2150                 name  :'yes',
2151                 weight : 'primary',
2152                 html : 'Yes'
2153             }
2154         ],
2155         
2156         /**
2157          * Button config that displays OK and Cancel buttons
2158          * @type Object
2159          */
2160         OKCANCEL : [
2161             {
2162                name : 'cancel',
2163                 html : 'Cancel'
2164             },
2165             {
2166                 name : 'ok',
2167                 weight : 'primary',
2168                 html : 'OK'
2169             }
2170         ],
2171         /**
2172          * Button config that displays Yes, No and Cancel buttons
2173          * @type Object
2174          */
2175         YESNOCANCEL : [
2176             {
2177                 name : 'yes',
2178                 weight : 'primary',
2179                 html : 'Yes'
2180             },
2181             {
2182                 name : 'no',
2183                 html : 'No'
2184             },
2185             {
2186                 name : 'cancel',
2187                 html : 'Cancel'
2188             }
2189         ]
2190 });
2191  /*
2192  * - LGPL
2193  *
2194  * messagebox - can be used as a replace
2195  * 
2196  */
2197 /**
2198  * @class Roo.MessageBox
2199  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2200  * Example usage:
2201  *<pre><code>
2202 // Basic alert:
2203 Roo.Msg.alert('Status', 'Changes saved successfully.');
2204
2205 // Prompt for user data:
2206 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2207     if (btn == 'ok'){
2208         // process text value...
2209     }
2210 });
2211
2212 // Show a dialog using config options:
2213 Roo.Msg.show({
2214    title:'Save Changes?',
2215    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2216    buttons: Roo.Msg.YESNOCANCEL,
2217    fn: processResult,
2218    animEl: 'elId'
2219 });
2220 </code></pre>
2221  * @singleton
2222  */
2223 Roo.bootstrap.MessageBox = function(){
2224     var dlg, opt, mask, waitTimer;
2225     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2226     var buttons, activeTextEl, bwidth;
2227
2228     
2229     // private
2230     var handleButton = function(button){
2231         dlg.hide();
2232         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2233     };
2234
2235     // private
2236     var handleHide = function(){
2237         if(opt && opt.cls){
2238             dlg.el.removeClass(opt.cls);
2239         }
2240         //if(waitTimer){
2241         //    Roo.TaskMgr.stop(waitTimer);
2242         //    waitTimer = null;
2243         //}
2244     };
2245
2246     // private
2247     var updateButtons = function(b){
2248         var width = 0;
2249         if(!b){
2250             buttons["ok"].hide();
2251             buttons["cancel"].hide();
2252             buttons["yes"].hide();
2253             buttons["no"].hide();
2254             //dlg.footer.dom.style.display = 'none';
2255             return width;
2256         }
2257         dlg.footer.dom.style.display = '';
2258         for(var k in buttons){
2259             if(typeof buttons[k] != "function"){
2260                 if(b[k]){
2261                     buttons[k].show();
2262                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2263                     width += buttons[k].el.getWidth()+15;
2264                 }else{
2265                     buttons[k].hide();
2266                 }
2267             }
2268         }
2269         return width;
2270     };
2271
2272     // private
2273     var handleEsc = function(d, k, e){
2274         if(opt && opt.closable !== false){
2275             dlg.hide();
2276         }
2277         if(e){
2278             e.stopEvent();
2279         }
2280     };
2281
2282     return {
2283         /**
2284          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2285          * @return {Roo.BasicDialog} The BasicDialog element
2286          */
2287         getDialog : function(){
2288            if(!dlg){
2289                 dlg = new Roo.bootstrap.Modal( {
2290                     //draggable: true,
2291                     //resizable:false,
2292                     //constraintoviewport:false,
2293                     //fixedcenter:true,
2294                     //collapsible : false,
2295                     //shim:true,
2296                     //modal: true,
2297                   //  width:400,
2298                   //  height:100,
2299                     //buttonAlign:"center",
2300                     closeClick : function(){
2301                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2302                             handleButton("no");
2303                         }else{
2304                             handleButton("cancel");
2305                         }
2306                     }
2307                 });
2308                 dlg.render();
2309                 dlg.on("hide", handleHide);
2310                 mask = dlg.mask;
2311                 //dlg.addKeyListener(27, handleEsc);
2312                 buttons = {};
2313                 this.buttons = buttons;
2314                 var bt = this.buttonText;
2315                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2316                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2317                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2318                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2319                 Roo.log(buttons)
2320                 bodyEl = dlg.body.createChild({
2321
2322                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2323                         '<textarea class="roo-mb-textarea"></textarea>' +
2324                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
2325                 });
2326                 msgEl = bodyEl.dom.firstChild;
2327                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2328                 textboxEl.enableDisplayMode();
2329                 textboxEl.addKeyListener([10,13], function(){
2330                     if(dlg.isVisible() && opt && opt.buttons){
2331                         if(opt.buttons.ok){
2332                             handleButton("ok");
2333                         }else if(opt.buttons.yes){
2334                             handleButton("yes");
2335                         }
2336                     }
2337                 });
2338                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2339                 textareaEl.enableDisplayMode();
2340                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2341                 progressEl.enableDisplayMode();
2342                 var pf = progressEl.dom.firstChild;
2343                 if (pf) {
2344                     pp = Roo.get(pf.firstChild);
2345                     pp.setHeight(pf.offsetHeight);
2346                 }
2347                 
2348             }
2349             return dlg;
2350         },
2351
2352         /**
2353          * Updates the message box body text
2354          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2355          * the XHTML-compliant non-breaking space character '&amp;#160;')
2356          * @return {Roo.MessageBox} This message box
2357          */
2358         updateText : function(text){
2359             if(!dlg.isVisible() && !opt.width){
2360                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2361             }
2362             msgEl.innerHTML = text || '&#160;';
2363       
2364             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2365             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2366             var w = Math.max(
2367                     Math.min(opt.width || cw , this.maxWidth), 
2368                     Math.max(opt.minWidth || this.minWidth, bwidth)
2369             );
2370             if(opt.prompt){
2371                 activeTextEl.setWidth(w);
2372             }
2373             if(dlg.isVisible()){
2374                 dlg.fixedcenter = false;
2375             }
2376             // to big, make it scroll. = But as usual stupid IE does not support
2377             // !important..
2378             
2379             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2380                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2381                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2382             } else {
2383                 bodyEl.dom.style.height = '';
2384                 bodyEl.dom.style.overflowY = '';
2385             }
2386             if (cw > w) {
2387                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2388             } else {
2389                 bodyEl.dom.style.overflowX = '';
2390             }
2391             
2392             dlg.setContentSize(w, bodyEl.getHeight());
2393             if(dlg.isVisible()){
2394                 dlg.fixedcenter = true;
2395             }
2396             return this;
2397         },
2398
2399         /**
2400          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
2401          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2402          * @param {Number} value Any number between 0 and 1 (e.g., .5)
2403          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2404          * @return {Roo.MessageBox} This message box
2405          */
2406         updateProgress : function(value, text){
2407             if(text){
2408                 this.updateText(text);
2409             }
2410             if (pp) { // weird bug on my firefox - for some reason this is not defined
2411                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2412             }
2413             return this;
2414         },        
2415
2416         /**
2417          * Returns true if the message box is currently displayed
2418          * @return {Boolean} True if the message box is visible, else false
2419          */
2420         isVisible : function(){
2421             return dlg && dlg.isVisible();  
2422         },
2423
2424         /**
2425          * Hides the message box if it is displayed
2426          */
2427         hide : function(){
2428             if(this.isVisible()){
2429                 dlg.hide();
2430             }  
2431         },
2432
2433         /**
2434          * Displays a new message box, or reinitializes an existing message box, based on the config options
2435          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2436          * The following config object properties are supported:
2437          * <pre>
2438 Property    Type             Description
2439 ----------  ---------------  ------------------------------------------------------------------------------------
2440 animEl            String/Element   An id or Element from which the message box should animate as it opens and
2441                                    closes (defaults to undefined)
2442 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
2443                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
2444 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
2445                                    progress and wait dialogs will ignore this property and always hide the
2446                                    close button as they can only be closed programmatically.
2447 cls               String           A custom CSS class to apply to the message box element
2448 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
2449                                    displayed (defaults to 75)
2450 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
2451                                    function will be btn (the name of the button that was clicked, if applicable,
2452                                    e.g. "ok"), and text (the value of the active text field, if applicable).
2453                                    Progress and wait dialogs will ignore this option since they do not respond to
2454                                    user actions and can only be closed programmatically, so any required function
2455                                    should be called by the same code after it closes the dialog.
2456 icon              String           A CSS class that provides a background image to be used as an icon for
2457                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
2458 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
2459 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
2460 modal             Boolean          False to allow user interaction with the page while the message box is
2461                                    displayed (defaults to true)
2462 msg               String           A string that will replace the existing message box body text (defaults
2463                                    to the XHTML-compliant non-breaking space character '&#160;')
2464 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
2465 progress          Boolean          True to display a progress bar (defaults to false)
2466 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
2467 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
2468 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
2469 title             String           The title text
2470 value             String           The string value to set into the active textbox element if displayed
2471 wait              Boolean          True to display a progress bar (defaults to false)
2472 width             Number           The width of the dialog in pixels
2473 </pre>
2474          *
2475          * Example usage:
2476          * <pre><code>
2477 Roo.Msg.show({
2478    title: 'Address',
2479    msg: 'Please enter your address:',
2480    width: 300,
2481    buttons: Roo.MessageBox.OKCANCEL,
2482    multiline: true,
2483    fn: saveAddress,
2484    animEl: 'addAddressBtn'
2485 });
2486 </code></pre>
2487          * @param {Object} config Configuration options
2488          * @return {Roo.MessageBox} This message box
2489          */
2490         show : function(options)
2491         {
2492             
2493             // this causes nightmares if you show one dialog after another
2494             // especially on callbacks..
2495              
2496             if(this.isVisible()){
2497                 
2498                 this.hide();
2499                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
2500                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
2501                 Roo.log("New Dialog Message:" +  options.msg )
2502                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
2503                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
2504                 
2505             }
2506             var d = this.getDialog();
2507             opt = options;
2508             d.setTitle(opt.title || "&#160;");
2509             d.close.setDisplayed(opt.closable !== false);
2510             activeTextEl = textboxEl;
2511             opt.prompt = opt.prompt || (opt.multiline ? true : false);
2512             if(opt.prompt){
2513                 if(opt.multiline){
2514                     textboxEl.hide();
2515                     textareaEl.show();
2516                     textareaEl.setHeight(typeof opt.multiline == "number" ?
2517                         opt.multiline : this.defaultTextHeight);
2518                     activeTextEl = textareaEl;
2519                 }else{
2520                     textboxEl.show();
2521                     textareaEl.hide();
2522                 }
2523             }else{
2524                 textboxEl.hide();
2525                 textareaEl.hide();
2526             }
2527             progressEl.setDisplayed(opt.progress === true);
2528             this.updateProgress(0);
2529             activeTextEl.dom.value = opt.value || "";
2530             if(opt.prompt){
2531                 dlg.setDefaultButton(activeTextEl);
2532             }else{
2533                 var bs = opt.buttons;
2534                 var db = null;
2535                 if(bs && bs.ok){
2536                     db = buttons["ok"];
2537                 }else if(bs && bs.yes){
2538                     db = buttons["yes"];
2539                 }
2540                 dlg.setDefaultButton(db);
2541             }
2542             bwidth = updateButtons(opt.buttons);
2543             this.updateText(opt.msg);
2544             if(opt.cls){
2545                 d.el.addClass(opt.cls);
2546             }
2547             d.proxyDrag = opt.proxyDrag === true;
2548             d.modal = opt.modal !== false;
2549             d.mask = opt.modal !== false ? mask : false;
2550             if(!d.isVisible()){
2551                 // force it to the end of the z-index stack so it gets a cursor in FF
2552                 document.body.appendChild(dlg.el.dom);
2553                 d.animateTarget = null;
2554                 d.show(options.animEl);
2555             }
2556             return this;
2557         },
2558
2559         /**
2560          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
2561          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
2562          * and closing the message box when the process is complete.
2563          * @param {String} title The title bar text
2564          * @param {String} msg The message box body text
2565          * @return {Roo.MessageBox} This message box
2566          */
2567         progress : function(title, msg){
2568             this.show({
2569                 title : title,
2570                 msg : msg,
2571                 buttons: false,
2572                 progress:true,
2573                 closable:false,
2574                 minWidth: this.minProgressWidth,
2575                 modal : true
2576             });
2577             return this;
2578         },
2579
2580         /**
2581          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
2582          * If a callback function is passed it will be called after the user clicks the button, and the
2583          * id of the button that was clicked will be passed as the only parameter to the callback
2584          * (could also be the top-right close button).
2585          * @param {String} title The title bar text
2586          * @param {String} msg The message box body text
2587          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2588          * @param {Object} scope (optional) The scope of the callback function
2589          * @return {Roo.MessageBox} This message box
2590          */
2591         alert : function(title, msg, fn, scope){
2592             this.show({
2593                 title : title,
2594                 msg : msg,
2595                 buttons: this.OK,
2596                 fn: fn,
2597                 scope : scope,
2598                 modal : true
2599             });
2600             return this;
2601         },
2602
2603         /**
2604          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
2605          * interaction while waiting for a long-running process to complete that does not have defined intervals.
2606          * You are responsible for closing the message box when the process is complete.
2607          * @param {String} msg The message box body text
2608          * @param {String} title (optional) The title bar text
2609          * @return {Roo.MessageBox} This message box
2610          */
2611         wait : function(msg, title){
2612             this.show({
2613                 title : title,
2614                 msg : msg,
2615                 buttons: false,
2616                 closable:false,
2617                 progress:true,
2618                 modal:true,
2619                 width:300,
2620                 wait:true
2621             });
2622             waitTimer = Roo.TaskMgr.start({
2623                 run: function(i){
2624                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
2625                 },
2626                 interval: 1000
2627             });
2628             return this;
2629         },
2630
2631         /**
2632          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
2633          * If a callback function is passed it will be called after the user clicks either button, and the id of the
2634          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
2635          * @param {String} title The title bar text
2636          * @param {String} msg The message box body text
2637          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2638          * @param {Object} scope (optional) The scope of the callback function
2639          * @return {Roo.MessageBox} This message box
2640          */
2641         confirm : function(title, msg, fn, scope){
2642             this.show({
2643                 title : title,
2644                 msg : msg,
2645                 buttons: this.YESNO,
2646                 fn: fn,
2647                 scope : scope,
2648                 modal : true
2649             });
2650             return this;
2651         },
2652
2653         /**
2654          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
2655          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
2656          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
2657          * (could also be the top-right close button) and the text that was entered will be passed as the two
2658          * parameters to the callback.
2659          * @param {String} title The title bar text
2660          * @param {String} msg The message box body text
2661          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2662          * @param {Object} scope (optional) The scope of the callback function
2663          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
2664          * property, or the height in pixels to create the textbox (defaults to false / single-line)
2665          * @return {Roo.MessageBox} This message box
2666          */
2667         prompt : function(title, msg, fn, scope, multiline){
2668             this.show({
2669                 title : title,
2670                 msg : msg,
2671                 buttons: this.OKCANCEL,
2672                 fn: fn,
2673                 minWidth:250,
2674                 scope : scope,
2675                 prompt:true,
2676                 multiline: multiline,
2677                 modal : true
2678             });
2679             return this;
2680         },
2681
2682         /**
2683          * Button config that displays a single OK button
2684          * @type Object
2685          */
2686         OK : {ok:true},
2687         /**
2688          * Button config that displays Yes and No buttons
2689          * @type Object
2690          */
2691         YESNO : {yes:true, no:true},
2692         /**
2693          * Button config that displays OK and Cancel buttons
2694          * @type Object
2695          */
2696         OKCANCEL : {ok:true, cancel:true},
2697         /**
2698          * Button config that displays Yes, No and Cancel buttons
2699          * @type Object
2700          */
2701         YESNOCANCEL : {yes:true, no:true, cancel:true},
2702
2703         /**
2704          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
2705          * @type Number
2706          */
2707         defaultTextHeight : 75,
2708         /**
2709          * The maximum width in pixels of the message box (defaults to 600)
2710          * @type Number
2711          */
2712         maxWidth : 600,
2713         /**
2714          * The minimum width in pixels of the message box (defaults to 100)
2715          * @type Number
2716          */
2717         minWidth : 100,
2718         /**
2719          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
2720          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
2721          * @type Number
2722          */
2723         minProgressWidth : 250,
2724         /**
2725          * An object containing the default button text strings that can be overriden for localized language support.
2726          * Supported properties are: ok, cancel, yes and no.
2727          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
2728          * @type Object
2729          */
2730         buttonText : {
2731             ok : "OK",
2732             cancel : "Cancel",
2733             yes : "Yes",
2734             no : "No"
2735         }
2736     };
2737 }();
2738
2739 /**
2740  * Shorthand for {@link Roo.MessageBox}
2741  */
2742 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox 
2743 Roo.Msg = Roo.Msg || Roo.MessageBox;
2744 /*
2745  * - LGPL
2746  *
2747  * navbar
2748  * 
2749  */
2750
2751 /**
2752  * @class Roo.bootstrap.Navbar
2753  * @extends Roo.bootstrap.Component
2754  * Bootstrap Navbar class
2755
2756  * @constructor
2757  * Create a new Navbar
2758  * @param {Object} config The config object
2759  */
2760
2761
2762 Roo.bootstrap.Navbar = function(config){
2763     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
2764     
2765 };
2766
2767 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
2768     
2769     
2770    
2771     // private
2772     navItems : false,
2773     loadMask : false,
2774     
2775     
2776     getAutoCreate : function(){
2777         
2778         
2779         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
2780         
2781     },
2782     
2783     initEvents :function ()
2784     {
2785         //Roo.log(this.el.select('.navbar-toggle',true));
2786         this.el.select('.navbar-toggle',true).on('click', function() {
2787            // Roo.log('click');
2788             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
2789         }, this);
2790         
2791         var mark = {
2792             tag: "div",
2793             cls:"x-dlg-mask"
2794         }
2795         
2796         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
2797         
2798         var size = this.el.getSize();
2799         this.maskEl.setSize(size.width, size.height);
2800         this.maskEl.enableDisplayMode("block");
2801         this.maskEl.hide();
2802         
2803         if(this.loadMask){
2804             this.maskEl.show();
2805         }
2806     },
2807     
2808     
2809     getChildContainer : function()
2810     {
2811         if (this.el.select('.collapse').getCount()) {
2812             return this.el.select('.collapse',true).first();
2813         }
2814         
2815         return this.el;
2816     },
2817     
2818     mask : function()
2819     {
2820         this.maskEl.show();
2821     },
2822     
2823     unmask : function()
2824     {
2825         this.maskEl.hide();
2826     }
2827     
2828     
2829     
2830 });
2831
2832
2833
2834  
2835
2836  /*
2837  * - LGPL
2838  *
2839  * navbar
2840  * 
2841  */
2842
2843 /**
2844  * @class Roo.bootstrap.NavSimplebar
2845  * @extends Roo.bootstrap.Navbar
2846  * Bootstrap Sidebar class
2847  *
2848  * @cfg {Boolean} inverse is inverted color
2849  * 
2850  * @cfg {String} type (nav | pills | tabs)
2851  * @cfg {Boolean} arrangement stacked | justified
2852  * @cfg {String} align (left | right) alignment
2853  * 
2854  * @cfg {Boolean} main (true|false) main nav bar? default false
2855  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
2856  * 
2857  * @cfg {String} tag (header|footer|nav|div) default is nav 
2858
2859  * 
2860  * 
2861  * 
2862  * @constructor
2863  * Create a new Sidebar
2864  * @param {Object} config The config object
2865  */
2866
2867
2868 Roo.bootstrap.NavSimplebar = function(config){
2869     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
2870 };
2871
2872 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
2873     
2874     inverse: false,
2875     
2876     type: false,
2877     arrangement: '',
2878     align : false,
2879     
2880     
2881     
2882     main : false,
2883     
2884     
2885     tag : false,
2886     
2887     
2888     getAutoCreate : function(){
2889         
2890         
2891         var cfg = {
2892             tag : this.tag || 'div',
2893             cls : 'navbar'
2894         };
2895           
2896         
2897         cfg.cn = [
2898             {
2899                 cls: 'nav',
2900                 tag : 'ul'
2901             }
2902         ];
2903         
2904          
2905         this.type = this.type || 'nav';
2906         if (['tabs','pills'].indexOf(this.type)!==-1) {
2907             cfg.cn[0].cls += ' nav-' + this.type
2908         
2909         
2910         } else {
2911             if (this.type!=='nav') {
2912                 Roo.log('nav type must be nav/tabs/pills')
2913             }
2914             cfg.cn[0].cls += ' navbar-nav'
2915         }
2916         
2917         
2918         
2919         
2920         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
2921             cfg.cn[0].cls += ' nav-' + this.arrangement;
2922         }
2923         
2924         
2925         if (this.align === 'right') {
2926             cfg.cn[0].cls += ' navbar-right';
2927         }
2928         
2929         if (this.inverse) {
2930             cfg.cls += ' navbar-inverse';
2931             
2932         }
2933         
2934         
2935         return cfg;
2936     
2937         
2938     }
2939     
2940     
2941     
2942 });
2943
2944
2945
2946  
2947
2948  
2949        /*
2950  * - LGPL
2951  *
2952  * navbar
2953  * 
2954  */
2955
2956 /**
2957  * @class Roo.bootstrap.NavHeaderbar
2958  * @extends Roo.bootstrap.NavSimplebar
2959  * Bootstrap Sidebar class
2960  *
2961  * @cfg {String} brand what is brand
2962  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
2963  * @cfg {String} brand_href href of the brand
2964  * 
2965  * @constructor
2966  * Create a new Sidebar
2967  * @param {Object} config The config object
2968  */
2969
2970
2971 Roo.bootstrap.NavHeaderbar = function(config){
2972     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
2973 };
2974
2975 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
2976     
2977     position: '',
2978     brand: '',
2979     brand_href: false,
2980     
2981     
2982     getAutoCreate : function(){
2983         
2984         
2985         
2986         var   cfg = {
2987             tag: this.nav || 'nav',
2988             cls: 'navbar',
2989             role: 'navigation',
2990             cn: [
2991                 {
2992                     tag: 'div',
2993                     cls: 'navbar-header',
2994                     cn: [
2995                         {
2996                         tag: 'button',
2997                         type: 'button',
2998                         cls: 'navbar-toggle',
2999                         'data-toggle': 'collapse',
3000                         cn: [
3001                             {
3002                                 tag: 'span',
3003                                 cls: 'sr-only',
3004                                 html: 'Toggle navigation'
3005                             },
3006                             {
3007                                 tag: 'span',
3008                                 cls: 'icon-bar'
3009                             },
3010                             {
3011                                 tag: 'span',
3012                                 cls: 'icon-bar'
3013                             },
3014                             {
3015                                 tag: 'span',
3016                                 cls: 'icon-bar'
3017                             }
3018                         ]
3019                         }
3020                     ]
3021                 },
3022                 {
3023                 tag: 'div',
3024                 cls: 'collapse navbar-collapse'
3025                 }
3026             ]
3027         };
3028         
3029         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3030         
3031         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3032             cfg.cls += ' navbar-' + this.position;
3033             
3034             // tag can override this..
3035             
3036             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3037         }
3038         
3039         if (this.brand !== '') {
3040             cfg.cn[0].cn.push({
3041                 tag: 'a',
3042                 href: this.brand_href ? this.brand_href : '#',
3043                 cls: 'navbar-brand',
3044                 cn: [
3045                 this.brand
3046                 ]
3047             });
3048         }
3049         
3050         if(this.main){
3051             cfg.cls += ' main-nav';
3052         }
3053         
3054         
3055         return cfg;
3056
3057         
3058     }
3059     
3060     
3061     
3062 });
3063
3064
3065
3066  
3067
3068  /*
3069  * - LGPL
3070  *
3071  * navbar
3072  * 
3073  */
3074
3075 /**
3076  * @class Roo.bootstrap.NavSidebar
3077  * @extends Roo.bootstrap.Navbar
3078  * Bootstrap Sidebar class
3079  * 
3080  * @constructor
3081  * Create a new Sidebar
3082  * @param {Object} config The config object
3083  */
3084
3085
3086 Roo.bootstrap.NavSidebar = function(config){
3087     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3088 };
3089
3090 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3091     
3092     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3093     
3094     getAutoCreate : function(){
3095         
3096         
3097         return  {
3098             tag: 'div',
3099             cls: 'sidebar sidebar-nav'
3100         };
3101     
3102         
3103     }
3104     
3105     
3106     
3107 });
3108
3109
3110
3111  
3112
3113  /*
3114  * - LGPL
3115  *
3116  * nav group
3117  * 
3118  */
3119
3120 /**
3121  * @class Roo.bootstrap.NavGroup
3122  * @extends Roo.bootstrap.Component
3123  * Bootstrap NavGroup class
3124  * @cfg {String} align left | right
3125  * @cfg {Boolean} inverse false | true
3126  * @cfg {String} type (nav|pills|tab) default nav
3127  * @cfg {String} navId - reference Id for navbar.
3128
3129  * 
3130  * @constructor
3131  * Create a new nav group
3132  * @param {Object} config The config object
3133  */
3134
3135 Roo.bootstrap.NavGroup = function(config){
3136     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3137     this.navItems = [];
3138     Roo.bootstrap.NavGroup.register(this);
3139      this.addEvents({
3140         /**
3141              * @event changed
3142              * Fires when the active item changes
3143              * @param {Roo.bootstrap.NavGroup} this
3144              * @param {Roo.bootstrap.Navbar.Item} item The item selected
3145              * @param {Roo.bootstrap.Navbar.Item} item The previously selected item 
3146          */
3147         'changed': true
3148      });
3149     
3150 };
3151
3152 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3153     
3154     align: '',
3155     inverse: false,
3156     form: false,
3157     type: 'nav',
3158     navId : '',
3159     // private
3160     
3161     navItems : false,
3162     
3163     getAutoCreate : function()
3164     {
3165         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3166         
3167         cfg = {
3168             tag : 'ul',
3169             cls: 'nav' 
3170         }
3171         
3172         if (['tabs','pills'].indexOf(this.type)!==-1) {
3173             cfg.cls += ' nav-' + this.type
3174         } else {
3175             if (this.type!=='nav') {
3176                 Roo.log('nav type must be nav/tabs/pills')
3177             }
3178             cfg.cls += ' navbar-nav'
3179         }
3180         
3181         if (this.parent().sidebar) {
3182             cfg = {
3183                 tag: 'ul',
3184                 cls: 'dashboard-menu sidebar-menu'
3185             }
3186             
3187             return cfg;
3188         }
3189         
3190         if (this.form === true) {
3191             cfg = {
3192                 tag: 'form',
3193                 cls: 'navbar-form'
3194             }
3195             
3196             if (this.align === 'right') {
3197                 cfg.cls += ' navbar-right';
3198             } else {
3199                 cfg.cls += ' navbar-left';
3200             }
3201         }
3202         
3203         if (this.align === 'right') {
3204             cfg.cls += ' navbar-right';
3205         }
3206         
3207         if (this.inverse) {
3208             cfg.cls += ' navbar-inverse';
3209             
3210         }
3211         
3212         
3213         return cfg;
3214     },
3215     
3216     setActiveItem : function(item)
3217     {
3218         var prev = false;
3219         Roo.each(this.navItems, function(v){
3220             if (v == item) {
3221                 return ;
3222             }
3223             if (v.isActive()) {
3224                 v.setActive(false, true);
3225                 prev = v;
3226                 
3227             }
3228             
3229         });
3230
3231         item.setActive(true, true);
3232         this.fireEvent('changed', this, item, prev);
3233         
3234         
3235     },
3236     
3237     
3238     register : function(item)
3239     {
3240         this.navItems.push( item);
3241         item.navId = this.navId;
3242     
3243     },
3244     getNavItem: function(tabId)
3245     {
3246         var ret = false;
3247         Roo.each(this.navItems, function(e) {
3248             if (e.tabId == tabId) {
3249                ret =  e;
3250                return false;
3251             }
3252             return true;
3253             
3254         });
3255         return ret;
3256     }
3257 });
3258
3259  
3260 Roo.apply(Roo.bootstrap.NavGroup, {
3261     
3262     groups: {},
3263     
3264     register : function(navgrp)
3265     {
3266         this.groups[navgrp.navId] = navgrp;
3267         
3268     },
3269     get: function(navId) {
3270         return this.groups[navId];
3271     }
3272     
3273     
3274     
3275 });
3276
3277  /*
3278  * - LGPL
3279  *
3280  * row
3281  * 
3282  */
3283
3284 /**
3285  * @class Roo.bootstrap.Navbar.Item
3286  * @extends Roo.bootstrap.Component
3287  * Bootstrap Navbar.Button class
3288  * @cfg {String} href  link to
3289  * @cfg {String} html content of button
3290  * @cfg {String} badge text inside badge
3291  * @cfg {String} glyphicon name of glyphicon
3292  * @cfg {String} icon name of font awesome icon
3293  * @cfg {Boolean} active Is item active
3294  * @cfg {Boolean} preventDefault (true | false) default false
3295  * @cfg {String} tabId the tab that this item activates.
3296   
3297  * @constructor
3298  * Create a new Navbar Button
3299  * @param {Object} config The config object
3300  */
3301 Roo.bootstrap.Navbar.Item = function(config){
3302     Roo.bootstrap.Navbar.Item.superclass.constructor.call(this, config);
3303     this.addEvents({
3304         // raw events
3305         /**
3306          * @event click
3307          * The raw click event for the entire grid.
3308          * @param {Roo.EventObject} e
3309          */
3310         "click" : true,
3311          /**
3312             * @event changed
3313             * Fires when the active item active state changes
3314             * @param {Roo.bootstrap.Navbar.Item} this
3315             * @param {boolean} state the new state
3316              
3317          */
3318         'changed': true
3319     });
3320    
3321 };
3322
3323 Roo.extend(Roo.bootstrap.Navbar.Item, Roo.bootstrap.Component,  {
3324     
3325     href: false,
3326     html: '',
3327     badge: '',
3328     icon: false,
3329     glyphicon: false,
3330     active: false,
3331     preventDefault : false,
3332     tabId : false,
3333     
3334     getAutoCreate : function(){
3335         
3336         var cfg = Roo.apply({}, Roo.bootstrap.Navbar.Item.superclass.getAutoCreate.call(this));
3337         
3338         if (this.parent().parent().sidebar === true) {
3339             cfg = {
3340                 tag: 'li',
3341                 cls: '',
3342                 cn: [
3343                     {
3344                     tag: 'p',
3345                     cls: ''
3346                     }
3347                 ]
3348             }
3349             
3350             if (this.html) {
3351                 cfg.cn[0].html = this.html;
3352             }
3353             
3354             if (this.active) {
3355                 this.cls += ' active';
3356             }
3357             
3358             if (this.menu) {
3359                 cfg.cn[0].cls += ' dropdown-toggle';
3360                 cfg.cn[0].html = (cfg.cn[0].html || this.html) + '<span class="glyphicon glyphicon-chevron-down"></span>';
3361             }
3362             
3363             if (this.href) {
3364                 cfg.cn[0].tag = 'a',
3365                 cfg.cn[0].href = this.href;
3366             }
3367             
3368             if (this.glyphicon) {
3369                 cfg.cn[0].html = '<i class="glyphicon glyphicon-'+this.glyphicon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
3370             }
3371                 
3372             if (this.icon) {
3373                 cfg.cn[0].html = '<i class="'+this.icon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
3374             }
3375             
3376             return cfg;
3377         }
3378         
3379         cfg = {
3380             tag: 'li',
3381                 cls: 'nav-item'
3382         }
3383             
3384         if (this.active) {
3385             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
3386         }
3387             
3388         cfg.cn = [
3389             {
3390                 tag: 'p',
3391                 html: 'Text'
3392             }
3393         ];
3394         
3395         if (this.glyphicon) {
3396             if(cfg.html){cfg.html = ' ' + this.html};
3397             cfg.cn=[
3398                 {
3399                     tag: 'span',
3400                     cls: 'glyphicon glyphicon-' + this.glyphicon
3401                 }
3402             ];
3403         }
3404         
3405         cfg.cn[0].html = this.html || cfg.cn[0].html ;
3406         
3407         if (this.menu) {
3408             cfg.cn[0].tag='a';
3409             cfg.cn[0].href='#';
3410             cfg.cn[0].html += " <span class='caret'></span>";
3411         //}else if (!this.href) {
3412         //    cfg.cn[0].tag='p';
3413         //    cfg.cn[0].cls='navbar-text';
3414         } else {
3415             cfg.cn[0].tag='a';
3416             cfg.cn[0].href=this.href||'#';
3417             cfg.cn[0].html=this.html;
3418         }
3419         
3420         if (this.badge !== '') {
3421             
3422             cfg.cn[0].cn=[
3423             cfg.cn[0].html + ' ',
3424             {
3425                 tag: 'span',
3426                 cls: 'badge',
3427                 html: this.badge
3428             }
3429             ];
3430             cfg.cn[0].html=''
3431         }
3432          
3433         if (this.icon) {
3434             cfg.cn[0].html = '<i class="'+this.icon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
3435         }
3436         
3437         return cfg;
3438     },
3439     initEvents: function() {
3440        // Roo.log('init events?');
3441        // Roo.log(this.el.dom);
3442         this.el.select('a',true).on('click', this.onClick, this);
3443         // at this point parent should be available..
3444         this.parent().register(this);
3445     },
3446     
3447     onClick : function(e)
3448     {
3449         if(this.preventDefault){
3450             e.preventDefault();
3451         }
3452         
3453         if (typeof (this.menu) != 'undefined') {
3454             this.menu.parentType = this.xtype;
3455             this.menu.triggerEl = this.el;
3456             this.addxtype(Roo.apply({}, this.menu));
3457         }
3458         
3459         if(this.fireEvent('click', this, e) === false){
3460             return;
3461         };
3462         
3463         if (['tabs','pills'].indexOf(this.parent().type)!==-1) {
3464              if (typeof(this.parent().setActiveItem) !== 'undefined') {
3465                 this.parent().setActiveItem(this);
3466             }
3467             
3468         } 
3469     },
3470     
3471     isActive: function () {
3472         return this.active
3473     },
3474     setActive : function(state, fire)
3475     {
3476         this.active = state;
3477         if (!state ) {
3478             this.el.removeClass('active');
3479         } else if (!this.el.hasClass('active')) {
3480             this.el.addClass('active');
3481         }
3482         if (fire) {
3483             this.fireEvent('changed', this, state);
3484         }
3485         
3486         
3487     }
3488      // this should not be here...
3489  
3490 });
3491  
3492
3493  /*
3494  * - LGPL
3495  *
3496  * row
3497  * 
3498  */
3499
3500 /**
3501  * @class Roo.bootstrap.NavItem
3502  * @extends Roo.bootstrap.Component
3503  * Bootstrap Navbar.NavItem class
3504  * @cfg {String} href  link to
3505  * @cfg {String} html content of button
3506  * @cfg {String} badge text inside badge
3507  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
3508  * @cfg {String} glyphicon name of glyphicon
3509  * @cfg {String} icon name of font awesome icon
3510  * @cfg {Boolean} active Is item active
3511  * @cfg {Boolean} preventDefault (true | false) default false
3512  * @cfg {String} tabId the tab that this item activates.
3513   
3514  * @constructor
3515  * Create a new Navbar Item
3516  * @param {Object} config The config object
3517  */
3518 Roo.bootstrap.NavItem = function(config){
3519     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
3520     this.addEvents({
3521         // raw events
3522         /**
3523          * @event click
3524          * The raw click event for the entire grid.
3525          * @param {Roo.EventObject} e
3526          */
3527         "click" : true,
3528          /**
3529             * @event changed
3530             * Fires when the active item active state changes
3531             * @param {Roo.bootstrap.NavItem} this
3532             * @param {boolean} state the new state
3533              
3534          */
3535         'changed': true
3536     });
3537    
3538 };
3539
3540 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
3541     
3542     href: false,
3543     html: '',
3544     badge: '',
3545     icon: false,
3546     glyphicon: false,
3547     active: false,
3548     preventDefault : false,
3549     tabId : false,
3550     
3551     getAutoCreate : function(){
3552          
3553         var cfg = {
3554             tag: 'li',
3555             cls: 'nav-item',
3556             cn : [
3557                 {
3558                     tag: 'a',
3559                     href : this.href || "#",
3560                     html: this.html || ''
3561                 }
3562             ]
3563         }
3564             
3565         if (this.active) {
3566             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
3567         }
3568             
3569         // glyphicon and icon go before content..
3570         if (this.glyphicon || this.icon) {
3571              if (this.icon) {
3572                 cfg.cn[0].html = '<i class="'+this.icon+'"></i><span>' + cfg.cn[0].html + '</span>'
3573             } else {
3574                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span>'  + cfg.cn[0].html;
3575             }
3576         }
3577         
3578         
3579         
3580         if (this.menu) {
3581             
3582             cfg.cn[0].html += " <span class='caret'></span>";
3583          
3584         }
3585         
3586         if (this.badge !== '') {
3587              
3588             cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
3589         }
3590         
3591         
3592         
3593         return cfg;
3594     },
3595     initEvents: function() {
3596        // Roo.log('init events?');
3597        // Roo.log(this.el.dom);
3598        if (typeof (this.menu) != 'undefined') {
3599             this.menu.parentType = this.xtype;
3600             this.menu.triggerEl = this.el;
3601             this.addxtype(Roo.apply({}, this.menu));
3602         }
3603
3604        
3605         this.el.select('a',true).on('click', this.onClick, this);
3606         // at this point parent should be available..
3607         this.parent().register(this);
3608     },
3609     
3610     onClick : function(e)
3611     {
3612         if(this.preventDefault){
3613             e.preventDefault();
3614         }
3615         
3616         if(this.fireEvent('click', this, e) === false){
3617             return;
3618         };
3619         
3620         if (['tabs','pills'].indexOf(this.parent().type)!==-1) {
3621              if (typeof(this.parent().setActiveItem) !== 'undefined') {
3622                 this.parent().setActiveItem(this);
3623             }
3624             
3625             
3626             
3627         } 
3628     },
3629     
3630     isActive: function () {
3631         return this.active
3632     },
3633     setActive : function(state, fire)
3634     {
3635         this.active = state;
3636         if (!state ) {
3637             this.el.removeClass('active');
3638         } else if (!this.el.hasClass('active')) {
3639             this.el.addClass('active');
3640         }
3641         if (fire) {
3642             this.fireEvent('changed', this, state);
3643         }
3644         
3645         
3646     }
3647      // this should not be here...
3648  
3649 });
3650  
3651
3652  /*
3653  * - LGPL
3654  *
3655  * sidebar item
3656  *
3657  *  li
3658  *    <span> icon </span>
3659  *    <span> text </span>
3660  *    <span>badge </span>
3661  */
3662
3663 /**
3664  * @class Roo.bootstrap.NavSidebarItem
3665  * @extends Roo.bootstrap.NavItem
3666  * Bootstrap Navbar.NavSidebarItem class
3667  * @constructor
3668  * Create a new Navbar Button
3669  * @param {Object} config The config object
3670  */
3671 Roo.bootstrap.NavSidebarItem = function(config){
3672     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
3673     this.addEvents({
3674         // raw events
3675         /**
3676          * @event click
3677          * The raw click event for the entire grid.
3678          * @param {Roo.EventObject} e
3679          */
3680         "click" : true,
3681          /**
3682             * @event changed
3683             * Fires when the active item active state changes
3684             * @param {Roo.bootstrap.NavSidebarItem} this
3685             * @param {boolean} state the new state
3686              
3687          */
3688         'changed': true
3689     });
3690    
3691 };
3692
3693 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
3694     
3695     
3696     getAutoCreate : function(){
3697         
3698         
3699         var a = {
3700                 tag: 'a',
3701                 href : this.href || '#',
3702                 cls: '',
3703                 html : '',
3704                 cn : []
3705         };
3706         var cfg = {
3707             tag: 'li',
3708             cls: '',
3709             cn: [ a ]
3710         }
3711         var span = {
3712             tag: 'span',
3713             html : this.html || ''
3714         }
3715         
3716         
3717         if (this.active) {
3718             cfg.cls += ' active';
3719         }
3720         
3721         // left icon..
3722         if (this.glyphicon || this.icon) {
3723             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
3724             a.cn.push({ tag : 'i', cls : c }) ;
3725         }
3726         // html..
3727         a.cn.push(span);
3728         // then badge..
3729         if (this.badge !== '') {
3730             a.cn.push({ tag: 'span',  cls : 'badge pull-right ' + (this.badgecls || ''), html: this.badge }); 
3731         }
3732         // fi
3733         if (this.menu) {
3734             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
3735             a.cls += 'dropdown-toggle treeview' ;
3736             
3737         }
3738         
3739         
3740         
3741         return cfg;
3742          
3743            
3744     }
3745    
3746      
3747  
3748 });
3749  
3750
3751  /*
3752  * - LGPL
3753  *
3754  * row
3755  * 
3756  */
3757
3758 /**
3759  * @class Roo.bootstrap.Row
3760  * @extends Roo.bootstrap.Component
3761  * Bootstrap Row class (contains columns...)
3762  * 
3763  * @constructor
3764  * Create a new Row
3765  * @param {Object} config The config object
3766  */
3767
3768 Roo.bootstrap.Row = function(config){
3769     Roo.bootstrap.Row.superclass.constructor.call(this, config);
3770 };
3771
3772 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
3773     
3774     getAutoCreate : function(){
3775        return {
3776             cls: 'row clearfix'
3777        };
3778     }
3779     
3780     
3781 });
3782
3783  
3784
3785  /*
3786  * - LGPL
3787  *
3788  * element
3789  * 
3790  */
3791
3792 /**
3793  * @class Roo.bootstrap.Element
3794  * @extends Roo.bootstrap.Component
3795  * Bootstrap Element class
3796  * @cfg {String} html contents of the element
3797  * @cfg {String} tag tag of the element
3798  * @cfg {String} cls class of the element
3799  * 
3800  * @constructor
3801  * Create a new Element
3802  * @param {Object} config The config object
3803  */
3804
3805 Roo.bootstrap.Element = function(config){
3806     Roo.bootstrap.Element.superclass.constructor.call(this, config);
3807 };
3808
3809 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
3810     
3811     tag: 'div',
3812     cls: '',
3813     html: '',
3814      
3815     
3816     getAutoCreate : function(){
3817         
3818         var cfg = {
3819             tag: this.tag,
3820             cls: this.cls,
3821             html: this.html
3822         }
3823         
3824         
3825         
3826         return cfg;
3827     }
3828    
3829 });
3830
3831  
3832
3833  /*
3834  * - LGPL
3835  *
3836  * pagination
3837  * 
3838  */
3839
3840 /**
3841  * @class Roo.bootstrap.Pagination
3842  * @extends Roo.bootstrap.Component
3843  * Bootstrap Pagination class
3844  * @cfg {String} size xs | sm | md | lg
3845  * @cfg {Boolean} inverse false | true
3846  * 
3847  * @constructor
3848  * Create a new Pagination
3849  * @param {Object} config The config object
3850  */
3851
3852 Roo.bootstrap.Pagination = function(config){
3853     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
3854 };
3855
3856 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
3857     
3858     cls: false,
3859     size: false,
3860     inverse: false,
3861     
3862     getAutoCreate : function(){
3863         var cfg = {
3864             tag: 'ul',
3865                 cls: 'pagination'
3866         };
3867         if (this.inverse) {
3868             cfg.cls += ' inverse';
3869         }
3870         if (this.html) {
3871             cfg.html=this.html;
3872         }
3873         if (this.cls) {
3874             cfg.cls += " " + this.cls;
3875         }
3876         return cfg;
3877     }
3878    
3879 });
3880
3881  
3882
3883  /*
3884  * - LGPL
3885  *
3886  * Pagination item
3887  * 
3888  */
3889
3890
3891 /**
3892  * @class Roo.bootstrap.PaginationItem
3893  * @extends Roo.bootstrap.Component
3894  * Bootstrap PaginationItem class
3895  * @cfg {String} html text
3896  * @cfg {String} href the link
3897  * @cfg {Boolean} preventDefault (true | false) default true
3898  * @cfg {Boolean} active (true | false) default false
3899  * 
3900  * 
3901  * @constructor
3902  * Create a new PaginationItem
3903  * @param {Object} config The config object
3904  */
3905
3906
3907 Roo.bootstrap.PaginationItem = function(config){
3908     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
3909     this.addEvents({
3910         // raw events
3911         /**
3912          * @event click
3913          * The raw click event for the entire grid.
3914          * @param {Roo.EventObject} e
3915          */
3916         "click" : true
3917     });
3918 };
3919
3920 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
3921     
3922     href : false,
3923     html : false,
3924     preventDefault: true,
3925     active : false,
3926     cls : false,
3927     
3928     getAutoCreate : function(){
3929         var cfg= {
3930             tag: 'li',
3931             cn: [
3932                 {
3933                     tag : 'a',
3934                     href : this.href ? this.href : '#',
3935                     html : this.html ? this.html : ''
3936                 }
3937             ]
3938         };
3939         
3940         if(this.cls){
3941             cfg.cls = this.cls;
3942         }
3943         
3944         if(this.active){
3945             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
3946         }
3947         
3948         return cfg;
3949     },
3950     
3951     initEvents: function() {
3952         
3953         this.el.on('click', this.onClick, this);
3954         
3955     },
3956     onClick : function(e)
3957     {
3958         Roo.log('PaginationItem on click ');
3959         if(this.preventDefault){
3960             e.preventDefault();
3961         }
3962         
3963         this.fireEvent('click', this, e);
3964     }
3965    
3966 });
3967
3968  
3969
3970  /*
3971  * - LGPL
3972  *
3973  * slider
3974  * 
3975  */
3976
3977
3978 /**
3979  * @class Roo.bootstrap.Slider
3980  * @extends Roo.bootstrap.Component
3981  * Bootstrap Slider class
3982  *    
3983  * @constructor
3984  * Create a new Slider
3985  * @param {Object} config The config object
3986  */
3987
3988 Roo.bootstrap.Slider = function(config){
3989     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
3990 };
3991
3992 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
3993     
3994     getAutoCreate : function(){
3995         
3996         var cfg = {
3997             tag: 'div',
3998             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
3999             cn: [
4000                 {
4001                     tag: 'a',
4002                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
4003                 }
4004             ]
4005         }
4006         
4007         return cfg;
4008     }
4009    
4010 });
4011
4012  /*
4013  * - LGPL
4014  *
4015  * table
4016  * 
4017  */
4018
4019 /**
4020  * @class Roo.bootstrap.Table
4021  * @extends Roo.bootstrap.Component
4022  * Bootstrap Table class
4023  * @cfg {String} cls table class
4024  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
4025  * @cfg {String} bgcolor Specifies the background color for a table
4026  * @cfg {Number} border Specifies whether the table cells should have borders or not
4027  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
4028  * @cfg {Number} cellspacing Specifies the space between cells
4029  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
4030  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
4031  * @cfg {String} sortable Specifies that the table should be sortable
4032  * @cfg {String} summary Specifies a summary of the content of a table
4033  * @cfg {Number} width Specifies the width of a table
4034  * 
4035  * @cfg {boolean} striped Should the rows be alternative striped
4036  * @cfg {boolean} bordered Add borders to the table
4037  * @cfg {boolean} hover Add hover highlighting
4038  * @cfg {boolean} condensed Format condensed
4039  * @cfg {boolean} responsive Format condensed
4040  * @cfg {Boolean} loadMask (true|false) default false
4041  *
4042  
4043  
4044  * 
4045  * @constructor
4046  * Create a new Table
4047  * @param {Object} config The config object
4048  */
4049
4050 Roo.bootstrap.Table = function(config){
4051     Roo.bootstrap.Table.superclass.constructor.call(this, config);
4052     
4053     if (this.sm) {
4054         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
4055         this.sm = this.selModel;
4056         this.sm.xmodule = this.xmodule || false;
4057     }
4058     if (this.cm && typeof(this.cm.config) == 'undefined') {
4059         this.colModel = new Roo.bootstrap.Table.ColumnModel(this.cm);
4060         this.cm = this.colModel;
4061         this.cm.xmodule = this.xmodule || false;
4062     }
4063     if (this.store) {
4064         this.store= Roo.factory(this.store, Roo.data);
4065         this.ds = this.store;
4066         this.ds.xmodule = this.xmodule || false;
4067          
4068     }
4069 };
4070
4071 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
4072     
4073     cls: false,
4074     align: false,
4075     bgcolor: false,
4076     border: false,
4077     cellpadding: false,
4078     cellspacing: false,
4079     frame: false,
4080     rules: false,
4081     sortable: false,
4082     summary: false,
4083     width: false,
4084     striped : false,
4085     bordered: false,
4086     hover:  false,
4087     condensed : false,
4088     responsive : false,
4089     sm : false,
4090     cm : false,
4091     store : false,
4092     loadMask : false,
4093     
4094     getAutoCreate : function(){
4095         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
4096         
4097         cfg = {
4098             tag: 'table',
4099             cls : 'table',
4100             cn : []
4101         }
4102             
4103         if (this.striped) {
4104             cfg.cls += ' table-striped';
4105         }
4106         if (this.hover) {
4107             cfg.cls += ' table-hover';
4108         }
4109         if (this.bordered) {
4110             cfg.cls += ' table-bordered';
4111         }
4112         if (this.condensed) {
4113             cfg.cls += ' table-condensed';
4114         }
4115         if (this.responsive) {
4116             cfg.cls += ' table-responsive';
4117         }
4118         
4119           
4120         
4121         
4122         if (this.cls) {
4123             cfg.cls+=  ' ' +this.cls;
4124         }
4125         
4126         // this lot should be simplifed...
4127         
4128         if (this.align) {
4129             cfg.align=this.align;
4130         }
4131         if (this.bgcolor) {
4132             cfg.bgcolor=this.bgcolor;
4133         }
4134         if (this.border) {
4135             cfg.border=this.border;
4136         }
4137         if (this.cellpadding) {
4138             cfg.cellpadding=this.cellpadding;
4139         }
4140         if (this.cellspacing) {
4141             cfg.cellspacing=this.cellspacing;
4142         }
4143         if (this.frame) {
4144             cfg.frame=this.frame;
4145         }
4146         if (this.rules) {
4147             cfg.rules=this.rules;
4148         }
4149         if (this.sortable) {
4150             cfg.sortable=this.sortable;
4151         }
4152         if (this.summary) {
4153             cfg.summary=this.summary;
4154         }
4155         if (this.width) {
4156             cfg.width=this.width;
4157         }
4158         
4159         if(this.store || this.cm){
4160             cfg.cn.push(this.renderHeader());
4161             cfg.cn.push(this.renderBody());
4162             cfg.cn.push(this.renderFooter());
4163             
4164             cfg.cls+=  ' TableGrid';
4165         }
4166         
4167         return cfg;
4168     },
4169 //    
4170 //    initTableGrid : function()
4171 //    {
4172 //        var cfg = {};
4173 //        
4174 //        var header = {
4175 //            tag: 'thead',
4176 //            cn : []
4177 //        };
4178 //        
4179 //        var cm = this.cm;
4180 //        
4181 //        for(var i = 0, len = cm.getColumnCount(); i < len; i++){
4182 //            header.cn.push({
4183 //                tag: 'th',
4184 //                html: cm.getColumnHeader(i)
4185 //            })
4186 //        }
4187 //        
4188 //        cfg.push(header);
4189 //        
4190 //        return cfg;
4191 //        
4192 //        
4193 //    },
4194     
4195     initEvents : function()
4196     {   
4197         if(!this.store || !this.cm){
4198             return;
4199         }
4200         
4201         Roo.log('initEvents with ds!!!!');
4202         
4203         var _this = this;
4204         
4205         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
4206             e.on('click', _this.sort, _this);
4207         });
4208 //        this.maskEl = Roo.DomHelper.append(this.el.select('.TableGrid', true).first(), {tag: "div", cls:"x-dlg-mask"}, true);
4209 //        this.maskEl.enableDisplayMode("block");
4210 //        this.maskEl.show();
4211         
4212         this.parent().el.setStyle('position', 'relative');
4213         
4214         var mark = {
4215             tag: "div",
4216             cls:"x-dlg-mask",
4217             style: "text-align:center",
4218             cn: [
4219                 {
4220                     tag: "div",
4221                     style: "background-color:white;width:50%;",
4222                     cn: [
4223                         {
4224                             tag: "img",
4225                             src: rootURL + '/roojs1/images/ux/lightbox/loading.gif'
4226                         },
4227                         {
4228                             tag: "span",
4229                             html: "Loading"
4230                         }
4231                         
4232                     ]
4233                 }
4234             ]
4235         }
4236         this.maskEl = Roo.DomHelper.append(this.parent().el, mark, true);
4237         
4238         var size = this.parent().el.getSize();
4239         
4240         this.maskEl.setSize(size.width, size.height);
4241         this.maskEl.enableDisplayMode("block");
4242         
4243         this.store.on('load', this.onLoad, this);
4244         this.store.on('beforeload', this.onBeforeLoad, this);
4245         
4246         this.store.load();
4247         
4248         
4249         
4250     },
4251     
4252     sort : function(e,el)
4253     {
4254         var col = Roo.get(el)
4255         
4256         if(!col.hasClass('sortable')){
4257             return;
4258         }
4259         
4260         var sort = col.attr('sort');
4261         var dir = 'ASC';
4262         
4263         if(col.hasClass('glyphicon-arrow-up')){
4264             dir = 'DESC';
4265         }
4266         
4267         this.store.sortInfo = {field : sort, direction : dir};
4268         
4269         this.store.load();
4270     },
4271     
4272     renderHeader : function()
4273     {
4274         var header = {
4275             tag: 'thead',
4276             cn : []
4277         };
4278         
4279         var cm = this.cm;
4280         
4281         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
4282             
4283             var config = cm.config[i];
4284             
4285             var c = {
4286                 tag: 'th',
4287                 html: cm.getColumnHeader(i)
4288             };
4289             
4290             if(typeof(config.dataIndex) != 'undefined'){
4291                 c.sort = config.dataIndex;
4292             }
4293             
4294             if(typeof(config.sortable) != 'undefined' && config.sortable){
4295                 c.cls = 'sortable';
4296             }
4297             
4298             if(typeof(config.width) != 'undefined'){
4299                 c.style = 'width:' + config.width + 'px';
4300             }
4301             
4302             header.cn.push(c)
4303         }
4304         
4305         return header;
4306     },
4307     
4308     renderBody : function()
4309     {
4310         var body = {
4311             tag: 'tbody',
4312             cn : []
4313         };
4314         
4315         return body;
4316     },
4317     
4318     renderFooter : function()
4319     {
4320         var footer = {
4321             tag: 'tfoot',
4322             cn : []
4323         };
4324         
4325         return footer;
4326     },
4327     
4328     onLoad : function()
4329     {
4330         Roo.log('ds onload');
4331         
4332         var _this = this;
4333         var cm = this.cm;
4334         
4335         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
4336             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
4337             
4338             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
4339                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
4340             }
4341             
4342             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
4343                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
4344             }
4345         });
4346         
4347         var tbody = this.el.select('tbody', true).first();
4348         
4349         var renders = [];
4350         
4351         if(this.store.getCount() > 0){
4352             this.store.data.each(function(d){
4353                 var row = {
4354                     tag : 'tr',
4355                     cn : []
4356                 };
4357                 
4358                 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
4359                     var renderer = cm.getRenderer(i);
4360                     var config = cm.config[i];
4361                     var value = '';
4362                     var id = Roo.id();
4363                     
4364                     if(typeof(renderer) !== 'undefined'){
4365                         value = renderer(d.data[cm.getDataIndex(i)], false, d);
4366                     }
4367                     
4368                     if(typeof(value) === 'object'){
4369                         renders.push({
4370                             id : id,
4371                             cfg : value 
4372                         })
4373                     }
4374                     
4375                     var td = {
4376                         tag: 'td',
4377                         id: id,
4378                         html: (typeof(value) === 'object') ? '' : value
4379                     };
4380                     
4381                     if(typeof(config.width) != 'undefined'){
4382                         td.style = 'width:' +  config.width + 'px';
4383                     }
4384                     
4385                     row.cn.push(td);
4386                    
4387                 }
4388                 
4389                 tbody.createChild(row);
4390                 
4391             });
4392         }
4393         
4394         
4395         if(renders.length){
4396             var _this = this;
4397             Roo.each(renders, function(r){
4398                 _this.renderColumn(r);
4399             })
4400         }
4401 //        
4402 //        if(this.loadMask){
4403 //            this.maskEl.hide();
4404 //        }
4405     },
4406     
4407     onBeforeLoad : function()
4408     {
4409         Roo.log('ds onBeforeLoad');
4410         
4411         this.clear();
4412         
4413 //        if(this.loadMask){
4414 //            this.maskEl.show();
4415 //        }
4416     },
4417     
4418     clear : function()
4419     {
4420         this.el.select('tbody', true).first().dom.innerHTML = '';
4421     },
4422     
4423     getSelectionModel : function(){
4424         if(!this.selModel){
4425             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
4426         }
4427         return this.selModel;
4428     },
4429     
4430     renderColumn : function(r)
4431     {
4432         var _this = this;
4433         r.cfg.render(Roo.get(r.id));
4434         
4435         if(r.cfg.cn){
4436             Roo.each(r.cfg.cn, function(c){
4437                 var child = {
4438                     id: r.id,
4439                     cfg: c
4440                 }
4441                 _this.renderColumn(child);
4442             })
4443         }
4444     }
4445    
4446 });
4447
4448  
4449
4450  /*
4451  * - LGPL
4452  *
4453  * table cell
4454  * 
4455  */
4456
4457 /**
4458  * @class Roo.bootstrap.TableCell
4459  * @extends Roo.bootstrap.Component
4460  * Bootstrap TableCell class
4461  * @cfg {String} html cell contain text
4462  * @cfg {String} cls cell class
4463  * @cfg {String} tag cell tag (td|th) default td
4464  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
4465  * @cfg {String} align Aligns the content in a cell
4466  * @cfg {String} axis Categorizes cells
4467  * @cfg {String} bgcolor Specifies the background color of a cell
4468  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
4469  * @cfg {Number} colspan Specifies the number of columns a cell should span
4470  * @cfg {String} headers Specifies one or more header cells a cell is related to
4471  * @cfg {Number} height Sets the height of a cell
4472  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
4473  * @cfg {Number} rowspan Sets the number of rows a cell should span
4474  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
4475  * @cfg {String} valign Vertical aligns the content in a cell
4476  * @cfg {Number} width Specifies the width of a cell
4477  * 
4478  * @constructor
4479  * Create a new TableCell
4480  * @param {Object} config The config object
4481  */
4482
4483 Roo.bootstrap.TableCell = function(config){
4484     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
4485 };
4486
4487 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
4488     
4489     html: false,
4490     cls: false,
4491     tag: false,
4492     abbr: false,
4493     align: false,
4494     axis: false,
4495     bgcolor: false,
4496     charoff: false,
4497     colspan: false,
4498     headers: false,
4499     height: false,
4500     nowrap: false,
4501     rowspan: false,
4502     scope: false,
4503     valign: false,
4504     width: false,
4505     
4506     
4507     getAutoCreate : function(){
4508         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
4509         
4510         cfg = {
4511             tag: 'td'
4512         }
4513         
4514         if(this.tag){
4515             cfg.tag = this.tag;
4516         }
4517         
4518         if (this.html) {
4519             cfg.html=this.html
4520         }
4521         if (this.cls) {
4522             cfg.cls=this.cls
4523         }
4524         if (this.abbr) {
4525             cfg.abbr=this.abbr
4526         }
4527         if (this.align) {
4528             cfg.align=this.align
4529         }
4530         if (this.axis) {
4531             cfg.axis=this.axis
4532         }
4533         if (this.bgcolor) {
4534             cfg.bgcolor=this.bgcolor
4535         }
4536         if (this.charoff) {
4537             cfg.charoff=this.charoff
4538         }
4539         if (this.colspan) {
4540             cfg.colspan=this.colspan
4541         }
4542         if (this.headers) {
4543             cfg.headers=this.headers
4544         }
4545         if (this.height) {
4546             cfg.height=this.height
4547         }
4548         if (this.nowrap) {
4549             cfg.nowrap=this.nowrap
4550         }
4551         if (this.rowspan) {
4552             cfg.rowspan=this.rowspan
4553         }
4554         if (this.scope) {
4555             cfg.scope=this.scope
4556         }
4557         if (this.valign) {
4558             cfg.valign=this.valign
4559         }
4560         if (this.width) {
4561             cfg.width=this.width
4562         }
4563         
4564         
4565         return cfg;
4566     }
4567    
4568 });
4569
4570  
4571
4572  /*
4573  * - LGPL
4574  *
4575  * table row
4576  * 
4577  */
4578
4579 /**
4580  * @class Roo.bootstrap.TableRow
4581  * @extends Roo.bootstrap.Component
4582  * Bootstrap TableRow class
4583  * @cfg {String} cls row class
4584  * @cfg {String} align Aligns the content in a table row
4585  * @cfg {String} bgcolor Specifies a background color for a table row
4586  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
4587  * @cfg {String} valign Vertical aligns the content in a table row
4588  * 
4589  * @constructor
4590  * Create a new TableRow
4591  * @param {Object} config The config object
4592  */
4593
4594 Roo.bootstrap.TableRow = function(config){
4595     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
4596 };
4597
4598 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
4599     
4600     cls: false,
4601     align: false,
4602     bgcolor: false,
4603     charoff: false,
4604     valign: false,
4605     
4606     getAutoCreate : function(){
4607         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
4608         
4609         cfg = {
4610             tag: 'tr'
4611         }
4612             
4613         if(this.cls){
4614             cfg.cls = this.cls;
4615         }
4616         if(this.align){
4617             cfg.align = this.align;
4618         }
4619         if(this.bgcolor){
4620             cfg.bgcolor = this.bgcolor;
4621         }
4622         if(this.charoff){
4623             cfg.charoff = this.charoff;
4624         }
4625         if(this.valign){
4626             cfg.valign = this.valign;
4627         }
4628         
4629         return cfg;
4630     }
4631    
4632 });
4633
4634  
4635
4636  /*
4637  * - LGPL
4638  *
4639  * table body
4640  * 
4641  */
4642
4643 /**
4644  * @class Roo.bootstrap.TableBody
4645  * @extends Roo.bootstrap.Component
4646  * Bootstrap TableBody class
4647  * @cfg {String} cls element class
4648  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
4649  * @cfg {String} align Aligns the content inside the element
4650  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
4651  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
4652  * 
4653  * @constructor
4654  * Create a new TableBody
4655  * @param {Object} config The config object
4656  */
4657
4658 Roo.bootstrap.TableBody = function(config){
4659     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
4660 };
4661
4662 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
4663     
4664     cls: false,
4665     tag: false,
4666     align: false,
4667     charoff: false,
4668     valign: false,
4669     
4670     getAutoCreate : function(){
4671         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
4672         
4673         cfg = {
4674             tag: 'tbody'
4675         }
4676             
4677         if (this.cls) {
4678             cfg.cls=this.cls
4679         }
4680         if(this.tag){
4681             cfg.tag = this.tag;
4682         }
4683         
4684         if(this.align){
4685             cfg.align = this.align;
4686         }
4687         if(this.charoff){
4688             cfg.charoff = this.charoff;
4689         }
4690         if(this.valign){
4691             cfg.valign = this.valign;
4692         }
4693         
4694         return cfg;
4695     }
4696     
4697     
4698 //    initEvents : function()
4699 //    {
4700 //        
4701 //        if(!this.store){
4702 //            return;
4703 //        }
4704 //        
4705 //        this.store = Roo.factory(this.store, Roo.data);
4706 //        this.store.on('load', this.onLoad, this);
4707 //        
4708 //        this.store.load();
4709 //        
4710 //    },
4711 //    
4712 //    onLoad: function () 
4713 //    {   
4714 //        this.fireEvent('load', this);
4715 //    }
4716 //    
4717 //   
4718 });
4719
4720  
4721
4722  /*
4723  * Based on:
4724  * Ext JS Library 1.1.1
4725  * Copyright(c) 2006-2007, Ext JS, LLC.
4726  *
4727  * Originally Released Under LGPL - original licence link has changed is not relivant.
4728  *
4729  * Fork - LGPL
4730  * <script type="text/javascript">
4731  */
4732
4733 // as we use this in bootstrap.
4734 Roo.namespace('Roo.form');
4735  /**
4736  * @class Roo.form.Action
4737  * Internal Class used to handle form actions
4738  * @constructor
4739  * @param {Roo.form.BasicForm} el The form element or its id
4740  * @param {Object} config Configuration options
4741  */
4742
4743  
4744  
4745 // define the action interface
4746 Roo.form.Action = function(form, options){
4747     this.form = form;
4748     this.options = options || {};
4749 };
4750 /**
4751  * Client Validation Failed
4752  * @const 
4753  */
4754 Roo.form.Action.CLIENT_INVALID = 'client';
4755 /**
4756  * Server Validation Failed
4757  * @const 
4758  */
4759 Roo.form.Action.SERVER_INVALID = 'server';
4760  /**
4761  * Connect to Server Failed
4762  * @const 
4763  */
4764 Roo.form.Action.CONNECT_FAILURE = 'connect';
4765 /**
4766  * Reading Data from Server Failed
4767  * @const 
4768  */
4769 Roo.form.Action.LOAD_FAILURE = 'load';
4770
4771 Roo.form.Action.prototype = {
4772     type : 'default',
4773     failureType : undefined,
4774     response : undefined,
4775     result : undefined,
4776
4777     // interface method
4778     run : function(options){
4779
4780     },
4781
4782     // interface method
4783     success : function(response){
4784
4785     },
4786
4787     // interface method
4788     handleResponse : function(response){
4789
4790     },
4791
4792     // default connection failure
4793     failure : function(response){
4794         
4795         this.response = response;
4796         this.failureType = Roo.form.Action.CONNECT_FAILURE;
4797         this.form.afterAction(this, false);
4798     },
4799
4800     processResponse : function(response){
4801         this.response = response;
4802         if(!response.responseText){
4803             return true;
4804         }
4805         this.result = this.handleResponse(response);
4806         return this.result;
4807     },
4808
4809     // utility functions used internally
4810     getUrl : function(appendParams){
4811         var url = this.options.url || this.form.url || this.form.el.dom.action;
4812         if(appendParams){
4813             var p = this.getParams();
4814             if(p){
4815                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
4816             }
4817         }
4818         return url;
4819     },
4820
4821     getMethod : function(){
4822         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
4823     },
4824
4825     getParams : function(){
4826         var bp = this.form.baseParams;
4827         var p = this.options.params;
4828         if(p){
4829             if(typeof p == "object"){
4830                 p = Roo.urlEncode(Roo.applyIf(p, bp));
4831             }else if(typeof p == 'string' && bp){
4832                 p += '&' + Roo.urlEncode(bp);
4833             }
4834         }else if(bp){
4835             p = Roo.urlEncode(bp);
4836         }
4837         return p;
4838     },
4839
4840     createCallback : function(){
4841         return {
4842             success: this.success,
4843             failure: this.failure,
4844             scope: this,
4845             timeout: (this.form.timeout*1000),
4846             upload: this.form.fileUpload ? this.success : undefined
4847         };
4848     }
4849 };
4850
4851 Roo.form.Action.Submit = function(form, options){
4852     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
4853 };
4854
4855 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
4856     type : 'submit',
4857
4858     haveProgress : false,
4859     uploadComplete : false,
4860     
4861     // uploadProgress indicator.
4862     uploadProgress : function()
4863     {
4864         if (!this.form.progressUrl) {
4865             return;
4866         }
4867         
4868         if (!this.haveProgress) {
4869             Roo.MessageBox.progress("Uploading", "Uploading");
4870         }
4871         if (this.uploadComplete) {
4872            Roo.MessageBox.hide();
4873            return;
4874         }
4875         
4876         this.haveProgress = true;
4877    
4878         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
4879         
4880         var c = new Roo.data.Connection();
4881         c.request({
4882             url : this.form.progressUrl,
4883             params: {
4884                 id : uid
4885             },
4886             method: 'GET',
4887             success : function(req){
4888                //console.log(data);
4889                 var rdata = false;
4890                 var edata;
4891                 try  {
4892                    rdata = Roo.decode(req.responseText)
4893                 } catch (e) {
4894                     Roo.log("Invalid data from server..");
4895                     Roo.log(edata);
4896                     return;
4897                 }
4898                 if (!rdata || !rdata.success) {
4899                     Roo.log(rdata);
4900                     Roo.MessageBox.alert(Roo.encode(rdata));
4901                     return;
4902                 }
4903                 var data = rdata.data;
4904                 
4905                 if (this.uploadComplete) {
4906                    Roo.MessageBox.hide();
4907                    return;
4908                 }
4909                    
4910                 if (data){
4911                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
4912                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
4913                     );
4914                 }
4915                 this.uploadProgress.defer(2000,this);
4916             },
4917        
4918             failure: function(data) {
4919                 Roo.log('progress url failed ');
4920                 Roo.log(data);
4921             },
4922             scope : this
4923         });
4924            
4925     },
4926     
4927     
4928     run : function()
4929     {
4930         // run get Values on the form, so it syncs any secondary forms.
4931         this.form.getValues();
4932         
4933         var o = this.options;
4934         var method = this.getMethod();
4935         var isPost = method == 'POST';
4936         if(o.clientValidation === false || this.form.isValid()){
4937             
4938             if (this.form.progressUrl) {
4939                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
4940                     (new Date() * 1) + '' + Math.random());
4941                     
4942             } 
4943             
4944             
4945             Roo.Ajax.request(Roo.apply(this.createCallback(), {
4946                 form:this.form.el.dom,
4947                 url:this.getUrl(!isPost),
4948                 method: method,
4949                 params:isPost ? this.getParams() : null,
4950                 isUpload: this.form.fileUpload
4951             }));
4952             
4953             this.uploadProgress();
4954
4955         }else if (o.clientValidation !== false){ // client validation failed
4956             this.failureType = Roo.form.Action.CLIENT_INVALID;
4957             this.form.afterAction(this, false);
4958         }
4959     },
4960
4961     success : function(response)
4962     {
4963         this.uploadComplete= true;
4964         if (this.haveProgress) {
4965             Roo.MessageBox.hide();
4966         }
4967         
4968         
4969         var result = this.processResponse(response);
4970         if(result === true || result.success){
4971             this.form.afterAction(this, true);
4972             return;
4973         }
4974         if(result.errors){
4975             this.form.markInvalid(result.errors);
4976             this.failureType = Roo.form.Action.SERVER_INVALID;
4977         }
4978         this.form.afterAction(this, false);
4979     },
4980     failure : function(response)
4981     {
4982         this.uploadComplete= true;
4983         if (this.haveProgress) {
4984             Roo.MessageBox.hide();
4985         }
4986         
4987         this.response = response;
4988         this.failureType = Roo.form.Action.CONNECT_FAILURE;
4989         this.form.afterAction(this, false);
4990     },
4991     
4992     handleResponse : function(response){
4993         if(this.form.errorReader){
4994             var rs = this.form.errorReader.read(response);
4995             var errors = [];
4996             if(rs.records){
4997                 for(var i = 0, len = rs.records.length; i < len; i++) {
4998                     var r = rs.records[i];
4999                     errors[i] = r.data;
5000                 }
5001             }
5002             if(errors.length < 1){
5003                 errors = null;
5004             }
5005             return {
5006                 success : rs.success,
5007                 errors : errors
5008             };
5009         }
5010         var ret = false;
5011         try {
5012             ret = Roo.decode(response.responseText);
5013         } catch (e) {
5014             ret = {
5015                 success: false,
5016                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
5017                 errors : []
5018             };
5019         }
5020         return ret;
5021         
5022     }
5023 });
5024
5025
5026 Roo.form.Action.Load = function(form, options){
5027     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
5028     this.reader = this.form.reader;
5029 };
5030
5031 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
5032     type : 'load',
5033
5034     run : function(){
5035         
5036         Roo.Ajax.request(Roo.apply(
5037                 this.createCallback(), {
5038                     method:this.getMethod(),
5039                     url:this.getUrl(false),
5040                     params:this.getParams()
5041         }));
5042     },
5043
5044     success : function(response){
5045         
5046         var result = this.processResponse(response);
5047         if(result === true || !result.success || !result.data){
5048             this.failureType = Roo.form.Action.LOAD_FAILURE;
5049             this.form.afterAction(this, false);
5050             return;
5051         }
5052         this.form.clearInvalid();
5053         this.form.setValues(result.data);
5054         this.form.afterAction(this, true);
5055     },
5056
5057     handleResponse : function(response){
5058         if(this.form.reader){
5059             var rs = this.form.reader.read(response);
5060             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
5061             return {
5062                 success : rs.success,
5063                 data : data
5064             };
5065         }
5066         return Roo.decode(response.responseText);
5067     }
5068 });
5069
5070 Roo.form.Action.ACTION_TYPES = {
5071     'load' : Roo.form.Action.Load,
5072     'submit' : Roo.form.Action.Submit
5073 };/*
5074  * - LGPL
5075  *
5076  * form
5077  * 
5078  */
5079
5080 /**
5081  * @class Roo.bootstrap.Form
5082  * @extends Roo.bootstrap.Component
5083  * Bootstrap Form class
5084  * @cfg {String} method  GET | POST (default POST)
5085  * @cfg {String} labelAlign top | left (default top)
5086   * @cfg {String} align left  | right - for navbars
5087
5088  * 
5089  * @constructor
5090  * Create a new Form
5091  * @param {Object} config The config object
5092  */
5093
5094
5095 Roo.bootstrap.Form = function(config){
5096     Roo.bootstrap.Form.superclass.constructor.call(this, config);
5097     this.addEvents({
5098         /**
5099          * @event clientvalidation
5100          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
5101          * @param {Form} this
5102          * @param {Boolean} valid true if the form has passed client-side validation
5103          */
5104         clientvalidation: true,
5105         /**
5106          * @event beforeaction
5107          * Fires before any action is performed. Return false to cancel the action.
5108          * @param {Form} this
5109          * @param {Action} action The action to be performed
5110          */
5111         beforeaction: true,
5112         /**
5113          * @event actionfailed
5114          * Fires when an action fails.
5115          * @param {Form} this
5116          * @param {Action} action The action that failed
5117          */
5118         actionfailed : true,
5119         /**
5120          * @event actioncomplete
5121          * Fires when an action is completed.
5122          * @param {Form} this
5123          * @param {Action} action The action that completed
5124          */
5125         actioncomplete : true
5126     });
5127     
5128 };
5129
5130 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
5131       
5132      /**
5133      * @cfg {String} method
5134      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
5135      */
5136     method : 'POST',
5137     /**
5138      * @cfg {String} url
5139      * The URL to use for form actions if one isn't supplied in the action options.
5140      */
5141     /**
5142      * @cfg {Boolean} fileUpload
5143      * Set to true if this form is a file upload.
5144      */
5145      
5146     /**
5147      * @cfg {Object} baseParams
5148      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
5149      */
5150       
5151     /**
5152      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
5153      */
5154     timeout: 30,
5155     /**
5156      * @cfg {Sting} align (left|right) for navbar forms
5157      */
5158     align : 'left',
5159
5160     // private
5161     activeAction : null,
5162  
5163     /**
5164      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
5165      * element by passing it or its id or mask the form itself by passing in true.
5166      * @type Mixed
5167      */
5168     waitMsgTarget : false,
5169     
5170      
5171     
5172     /**
5173      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
5174      * element by passing it or its id or mask the form itself by passing in true.
5175      * @type Mixed
5176      */
5177     
5178     getAutoCreate : function(){
5179         
5180         var cfg = {
5181             tag: 'form',
5182             method : this.method || 'POST',
5183             id : this.id || Roo.id(),
5184             cls : ''
5185         }
5186         if (this.parent().xtype.match(/^Nav/)) {
5187             cfg.cls = 'navbar-form navbar-' + this.align;
5188             
5189         }
5190         
5191         if (this.labelAlign == 'left' ) {
5192             cfg.cls += ' form-horizontal';
5193         }
5194         
5195         
5196         return cfg;
5197     },
5198     initEvents : function()
5199     {
5200         this.el.on('submit', this.onSubmit, this);
5201         
5202         
5203     },
5204     // private
5205     onSubmit : function(e){
5206         e.stopEvent();
5207     },
5208     
5209      /**
5210      * Returns true if client-side validation on the form is successful.
5211      * @return Boolean
5212      */
5213     isValid : function(){
5214         var items = this.getItems();
5215         var valid = true;
5216         items.each(function(f){
5217            if(!f.validate()){
5218                valid = false;
5219                
5220            }
5221         });
5222         return valid;
5223     },
5224     /**
5225      * Returns true if any fields in this form have changed since their original load.
5226      * @return Boolean
5227      */
5228     isDirty : function(){
5229         var dirty = false;
5230         var items = this.getItems();
5231         items.each(function(f){
5232            if(f.isDirty()){
5233                dirty = true;
5234                return false;
5235            }
5236            return true;
5237         });
5238         return dirty;
5239     },
5240      /**
5241      * Performs a predefined action (submit or load) or custom actions you define on this form.
5242      * @param {String} actionName The name of the action type
5243      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
5244      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
5245      * accept other config options):
5246      * <pre>
5247 Property          Type             Description
5248 ----------------  ---------------  ----------------------------------------------------------------------------------
5249 url               String           The url for the action (defaults to the form's url)
5250 method            String           The form method to use (defaults to the form's method, or POST if not defined)
5251 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
5252 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
5253                                    validate the form on the client (defaults to false)
5254      * </pre>
5255      * @return {BasicForm} this
5256      */
5257     doAction : function(action, options){
5258         if(typeof action == 'string'){
5259             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
5260         }
5261         if(this.fireEvent('beforeaction', this, action) !== false){
5262             this.beforeAction(action);
5263             action.run.defer(100, action);
5264         }
5265         return this;
5266     },
5267     
5268     // private
5269     beforeAction : function(action){
5270         var o = action.options;
5271         
5272         // not really supported yet.. ??
5273         
5274         //if(this.waitMsgTarget === true){
5275             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
5276         //}else if(this.waitMsgTarget){
5277         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
5278         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
5279         //}else {
5280         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
5281        // }
5282          
5283     },
5284
5285     // private
5286     afterAction : function(action, success){
5287         this.activeAction = null;
5288         var o = action.options;
5289         
5290         //if(this.waitMsgTarget === true){
5291             this.el.unmask();
5292         //}else if(this.waitMsgTarget){
5293         //    this.waitMsgTarget.unmask();
5294         //}else{
5295         //    Roo.MessageBox.updateProgress(1);
5296         //    Roo.MessageBox.hide();
5297        // }
5298         // 
5299         if(success){
5300             if(o.reset){
5301                 this.reset();
5302             }
5303             Roo.callback(o.success, o.scope, [this, action]);
5304             this.fireEvent('actioncomplete', this, action);
5305             
5306         }else{
5307             
5308             // failure condition..
5309             // we have a scenario where updates need confirming.
5310             // eg. if a locking scenario exists..
5311             // we look for { errors : { needs_confirm : true }} in the response.
5312             if (
5313                 (typeof(action.result) != 'undefined')  &&
5314                 (typeof(action.result.errors) != 'undefined')  &&
5315                 (typeof(action.result.errors.needs_confirm) != 'undefined')
5316            ){
5317                 var _t = this;
5318                 Roo.log("not supported yet");
5319                  /*
5320                 
5321                 Roo.MessageBox.confirm(
5322                     "Change requires confirmation",
5323                     action.result.errorMsg,
5324                     function(r) {
5325                         if (r != 'yes') {
5326                             return;
5327                         }
5328                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
5329                     }
5330                     
5331                 );
5332                 */
5333                 
5334                 
5335                 return;
5336             }
5337             
5338             Roo.callback(o.failure, o.scope, [this, action]);
5339             // show an error message if no failed handler is set..
5340             if (!this.hasListener('actionfailed')) {
5341                 Roo.log("need to add dialog support");
5342                 /*
5343                 Roo.MessageBox.alert("Error",
5344                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
5345                         action.result.errorMsg :
5346                         "Saving Failed, please check your entries or try again"
5347                 );
5348                 */
5349             }
5350             
5351             this.fireEvent('actionfailed', this, action);
5352         }
5353         
5354     },
5355     /**
5356      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
5357      * @param {String} id The value to search for
5358      * @return Field
5359      */
5360     findField : function(id){
5361         var items = this.getItems();
5362         var field = items.get(id);
5363         if(!field){
5364              items.each(function(f){
5365                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
5366                     field = f;
5367                     return false;
5368                 }
5369                 return true;
5370             });
5371         }
5372         return field || null;
5373     },
5374      /**
5375      * Mark fields in this form invalid in bulk.
5376      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
5377      * @return {BasicForm} this
5378      */
5379     markInvalid : function(errors){
5380         if(errors instanceof Array){
5381             for(var i = 0, len = errors.length; i < len; i++){
5382                 var fieldError = errors[i];
5383                 var f = this.findField(fieldError.id);
5384                 if(f){
5385                     f.markInvalid(fieldError.msg);
5386                 }
5387             }
5388         }else{
5389             var field, id;
5390             for(id in errors){
5391                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
5392                     field.markInvalid(errors[id]);
5393                 }
5394             }
5395         }
5396         //Roo.each(this.childForms || [], function (f) {
5397         //    f.markInvalid(errors);
5398         //});
5399         
5400         return this;
5401     },
5402
5403     /**
5404      * Set values for fields in this form in bulk.
5405      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
5406      * @return {BasicForm} this
5407      */
5408     setValues : function(values){
5409         if(values instanceof Array){ // array of objects
5410             for(var i = 0, len = values.length; i < len; i++){
5411                 var v = values[i];
5412                 var f = this.findField(v.id);
5413                 if(f){
5414                     f.setValue(v.value);
5415                     if(this.trackResetOnLoad){
5416                         f.originalValue = f.getValue();
5417                     }
5418                 }
5419             }
5420         }else{ // object hash
5421             var field, id;
5422             for(id in values){
5423                 if(typeof values[id] != 'function' && (field = this.findField(id))){
5424                     
5425                     if (field.setFromData && 
5426                         field.valueField && 
5427                         field.displayField &&
5428                         // combos' with local stores can 
5429                         // be queried via setValue()
5430                         // to set their value..
5431                         (field.store && !field.store.isLocal)
5432                         ) {
5433                         // it's a combo
5434                         var sd = { };
5435                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
5436                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
5437                         field.setFromData(sd);
5438                         
5439                     } else {
5440                         field.setValue(values[id]);
5441                     }
5442                     
5443                     
5444                     if(this.trackResetOnLoad){
5445                         field.originalValue = field.getValue();
5446                     }
5447                 }
5448             }
5449         }
5450          
5451         //Roo.each(this.childForms || [], function (f) {
5452         //    f.setValues(values);
5453         //});
5454                 
5455         return this;
5456     },
5457
5458     /**
5459      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
5460      * they are returned as an array.
5461      * @param {Boolean} asString
5462      * @return {Object}
5463      */
5464     getValues : function(asString){
5465         //if (this.childForms) {
5466             // copy values from the child forms
5467         //    Roo.each(this.childForms, function (f) {
5468         //        this.setValues(f.getValues());
5469         //    }, this);
5470         //}
5471         
5472         
5473         
5474         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
5475         if(asString === true){
5476             return fs;
5477         }
5478         return Roo.urlDecode(fs);
5479     },
5480     
5481     /**
5482      * Returns the fields in this form as an object with key/value pairs. 
5483      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
5484      * @return {Object}
5485      */
5486     getFieldValues : function(with_hidden)
5487     {
5488         var items = this.getItems();
5489         var ret = {};
5490         items.each(function(f){
5491             if (!f.getName()) {
5492                 return;
5493             }
5494             var v = f.getValue();
5495             if (f.inputType =='radio') {
5496                 if (typeof(ret[f.getName()]) == 'undefined') {
5497                     ret[f.getName()] = ''; // empty..
5498                 }
5499                 
5500                 if (!f.el.dom.checked) {
5501                     return;
5502                     
5503                 }
5504                 v = f.el.dom.value;
5505                 
5506             }
5507             
5508             // not sure if this supported any more..
5509             if ((typeof(v) == 'object') && f.getRawValue) {
5510                 v = f.getRawValue() ; // dates..
5511             }
5512             // combo boxes where name != hiddenName...
5513             if (f.name != f.getName()) {
5514                 ret[f.name] = f.getRawValue();
5515             }
5516             ret[f.getName()] = v;
5517         });
5518         
5519         return ret;
5520     },
5521
5522     /**
5523      * Clears all invalid messages in this form.
5524      * @return {BasicForm} this
5525      */
5526     clearInvalid : function(){
5527         var items = this.getItems();
5528         
5529         items.each(function(f){
5530            f.clearInvalid();
5531         });
5532         
5533         
5534         
5535         return this;
5536     },
5537
5538     /**
5539      * Resets this form.
5540      * @return {BasicForm} this
5541      */
5542     reset : function(){
5543         var items = this.getItems();
5544         items.each(function(f){
5545             f.reset();
5546         });
5547         
5548         Roo.each(this.childForms || [], function (f) {
5549             f.reset();
5550         });
5551        
5552         
5553         return this;
5554     },
5555     getItems : function()
5556     {
5557         var r=new Roo.util.MixedCollection(false, function(o){
5558             return o.id || (o.id = Roo.id());
5559         });
5560         var iter = function(el) {
5561             if (el.inputEl) {
5562                 r.add(el);
5563             }
5564             if (!el.items) {
5565                 return;
5566             }
5567             Roo.each(el.items,function(e) {
5568                 iter(e);
5569             });
5570             
5571             
5572         };
5573         iter(this);
5574         return r;
5575         
5576         
5577         
5578         
5579     }
5580     
5581 });
5582
5583  
5584 /*
5585  * Based on:
5586  * Ext JS Library 1.1.1
5587  * Copyright(c) 2006-2007, Ext JS, LLC.
5588  *
5589  * Originally Released Under LGPL - original licence link has changed is not relivant.
5590  *
5591  * Fork - LGPL
5592  * <script type="text/javascript">
5593  */
5594 /**
5595  * @class Roo.form.VTypes
5596  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
5597  * @singleton
5598  */
5599 Roo.form.VTypes = function(){
5600     // closure these in so they are only created once.
5601     var alpha = /^[a-zA-Z_]+$/;
5602     var alphanum = /^[a-zA-Z0-9_]+$/;
5603     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
5604     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
5605
5606     // All these messages and functions are configurable
5607     return {
5608         /**
5609          * The function used to validate email addresses
5610          * @param {String} value The email address
5611          */
5612         'email' : function(v){
5613             return email.test(v);
5614         },
5615         /**
5616          * The error text to display when the email validation function returns false
5617          * @type String
5618          */
5619         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
5620         /**
5621          * The keystroke filter mask to be applied on email input
5622          * @type RegExp
5623          */
5624         'emailMask' : /[a-z0-9_\.\-@]/i,
5625
5626         /**
5627          * The function used to validate URLs
5628          * @param {String} value The URL
5629          */
5630         'url' : function(v){
5631             return url.test(v);
5632         },
5633         /**
5634          * The error text to display when the url validation function returns false
5635          * @type String
5636          */
5637         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
5638         
5639         /**
5640          * The function used to validate alpha values
5641          * @param {String} value The value
5642          */
5643         'alpha' : function(v){
5644             return alpha.test(v);
5645         },
5646         /**
5647          * The error text to display when the alpha validation function returns false
5648          * @type String
5649          */
5650         'alphaText' : 'This field should only contain letters and _',
5651         /**
5652          * The keystroke filter mask to be applied on alpha input
5653          * @type RegExp
5654          */
5655         'alphaMask' : /[a-z_]/i,
5656
5657         /**
5658          * The function used to validate alphanumeric values
5659          * @param {String} value The value
5660          */
5661         'alphanum' : function(v){
5662             return alphanum.test(v);
5663         },
5664         /**
5665          * The error text to display when the alphanumeric validation function returns false
5666          * @type String
5667          */
5668         'alphanumText' : 'This field should only contain letters, numbers and _',
5669         /**
5670          * The keystroke filter mask to be applied on alphanumeric input
5671          * @type RegExp
5672          */
5673         'alphanumMask' : /[a-z0-9_]/i
5674     };
5675 }();/*
5676  * - LGPL
5677  *
5678  * Input
5679  * 
5680  */
5681
5682 /**
5683  * @class Roo.bootstrap.Input
5684  * @extends Roo.bootstrap.Component
5685  * Bootstrap Input class
5686  * @cfg {Boolean} disabled is it disabled
5687  * @cfg {String} fieldLabel - the label associated
5688  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
5689  * @cfg {String} name name of the input
5690  * @cfg {string} fieldLabel - the label associated
5691  * @cfg {string}  inputType - input / file submit ...
5692  * @cfg {string} placeholder - placeholder to put in text.
5693  * @cfg {string}  before - input group add on before
5694  * @cfg {string} after - input group add on after
5695  * @cfg {string} size - (lg|sm) or leave empty..
5696  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
5697  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
5698  * @cfg {Number} md colspan out of 12 for computer-sized screens
5699  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
5700  * @cfg {string} value default value of the input
5701  * @cfg {Number} labelWidth set the width of label (0-12)
5702  * @cfg {String} labelAlign (top|left)
5703  * @cfg {Boolean} readOnly Specifies that the field should be read-only
5704  * 
5705  * 
5706  * @constructor
5707  * Create a new Input
5708  * @param {Object} config The config object
5709  */
5710
5711 Roo.bootstrap.Input = function(config){
5712     Roo.bootstrap.Input.superclass.constructor.call(this, config);
5713    
5714         this.addEvents({
5715             /**
5716              * @event focus
5717              * Fires when this field receives input focus.
5718              * @param {Roo.form.Field} this
5719              */
5720             focus : true,
5721             /**
5722              * @event blur
5723              * Fires when this field loses input focus.
5724              * @param {Roo.form.Field} this
5725              */
5726             blur : true,
5727             /**
5728              * @event specialkey
5729              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
5730              * {@link Roo.EventObject#getKey} to determine which key was pressed.
5731              * @param {Roo.form.Field} this
5732              * @param {Roo.EventObject} e The event object
5733              */
5734             specialkey : true,
5735             /**
5736              * @event change
5737              * Fires just before the field blurs if the field value has changed.
5738              * @param {Roo.form.Field} this
5739              * @param {Mixed} newValue The new value
5740              * @param {Mixed} oldValue The original value
5741              */
5742             change : true,
5743             /**
5744              * @event invalid
5745              * Fires after the field has been marked as invalid.
5746              * @param {Roo.form.Field} this
5747              * @param {String} msg The validation message
5748              */
5749             invalid : true,
5750             /**
5751              * @event valid
5752              * Fires after the field has been validated with no errors.
5753              * @param {Roo.form.Field} this
5754              */
5755             valid : true,
5756              /**
5757              * @event keyup
5758              * Fires after the key up
5759              * @param {Roo.form.Field} this
5760              * @param {Roo.EventObject}  e The event Object
5761              */
5762             keyup : true
5763         });
5764 };
5765
5766 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
5767      /**
5768      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
5769       automatic validation (defaults to "keyup").
5770      */
5771     validationEvent : "keyup",
5772      /**
5773      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
5774      */
5775     validateOnBlur : true,
5776     /**
5777      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
5778      */
5779     validationDelay : 250,
5780      /**
5781      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
5782      */
5783     focusClass : "x-form-focus",  // not needed???
5784     
5785        
5786     /**
5787      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
5788      */
5789     invalidClass : "has-error",
5790     
5791     /**
5792      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
5793      */
5794     selectOnFocus : false,
5795     
5796      /**
5797      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
5798      */
5799     maskRe : null,
5800        /**
5801      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
5802      */
5803     vtype : null,
5804     
5805       /**
5806      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
5807      */
5808     disableKeyFilter : false,
5809     
5810        /**
5811      * @cfg {Boolean} disabled True to disable the field (defaults to false).
5812      */
5813     disabled : false,
5814      /**
5815      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
5816      */
5817     allowBlank : true,
5818     /**
5819      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
5820      */
5821     blankText : "This field is required",
5822     
5823      /**
5824      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
5825      */
5826     minLength : 0,
5827     /**
5828      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
5829      */
5830     maxLength : Number.MAX_VALUE,
5831     /**
5832      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
5833      */
5834     minLengthText : "The minimum length for this field is {0}",
5835     /**
5836      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
5837      */
5838     maxLengthText : "The maximum length for this field is {0}",
5839   
5840     
5841     /**
5842      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
5843      * If available, this function will be called only after the basic validators all return true, and will be passed the
5844      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
5845      */
5846     validator : null,
5847     /**
5848      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
5849      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
5850      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
5851      */
5852     regex : null,
5853     /**
5854      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
5855      */
5856     regexText : "",
5857     
5858     
5859     
5860     fieldLabel : '',
5861     inputType : 'text',
5862     
5863     name : false,
5864     placeholder: false,
5865     before : false,
5866     after : false,
5867     size : false,
5868     // private
5869     hasFocus : false,
5870     preventMark: false,
5871     isFormField : true,
5872     value : '',
5873     labelWidth : 2,
5874     labelAlign : false,
5875     readOnly : false,
5876     
5877     parentLabelAlign : function()
5878     {
5879         var parent = this;
5880         while (parent.parent()) {
5881             parent = parent.parent();
5882             if (typeof(parent.labelAlign) !='undefined') {
5883                 return parent.labelAlign;
5884             }
5885         }
5886         return 'left';
5887         
5888     },
5889     
5890     getAutoCreate : function(){
5891         
5892         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
5893         
5894         var id = Roo.id();
5895         
5896         var cfg = {};
5897         
5898         if(this.inputType != 'hidden'){
5899             cfg.cls = 'form-group' //input-group
5900         }
5901         
5902         var input =  {
5903             tag: 'input',
5904             id : id,
5905             type : this.inputType,
5906             value : this.value,
5907             cls : 'form-control',
5908             placeholder : this.placeholder || ''
5909             
5910         };
5911         
5912         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
5913             input.maxLength = this.maxLength;
5914         }
5915         
5916         if (this.disabled) {
5917             input.disabled=true;
5918         }
5919         
5920         if (this.readOnly) {
5921             input.readonly=true;
5922         }
5923         
5924         if (this.name) {
5925             input.name = this.name;
5926         }
5927         if (this.size) {
5928             input.cls += ' input-' + this.size;
5929         }
5930         var settings=this;
5931         ['xs','sm','md','lg'].map(function(size){
5932             if (settings[size]) {
5933                 cfg.cls += ' col-' + size + '-' + settings[size];
5934             }
5935         });
5936         
5937         var inputblock = input;
5938         
5939         if (this.before || this.after) {
5940             
5941             inputblock = {
5942                 cls : 'input-group',
5943                 cn :  [] 
5944             };
5945             if (this.before && typeof(this.before) == 'string') {
5946                 
5947                 inputblock.cn.push({
5948                     tag :'span',
5949                     cls : 'roo-input-before input-group-addon',
5950                     html : this.before
5951                 });
5952             }
5953             if (this.before && typeof(this.before) == 'object') {
5954                 this.before = Roo.factory(this.before);
5955                 Roo.log(this.before);
5956                 inputblock.cn.push({
5957                     tag :'span',
5958                     cls : 'roo-input-before input-group-' +
5959                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
5960                 });
5961             }
5962             
5963             inputblock.cn.push(input);
5964             
5965             if (this.after && typeof(this.after) == 'string') {
5966                 inputblock.cn.push({
5967                     tag :'span',
5968                     cls : 'roo-input-after input-group-addon',
5969                     html : this.after
5970                 });
5971             }
5972             if (this.after && typeof(this.after) == 'object') {
5973                 this.after = Roo.factory(this.after);
5974                 Roo.log(this.after);
5975                 inputblock.cn.push({
5976                     tag :'span',
5977                     cls : 'roo-input-after input-group-' +
5978                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
5979                 });
5980             }
5981         };
5982         
5983         if (align ==='left' && this.fieldLabel.length) {
5984                 Roo.log("left and has label");
5985                 cfg.cn = [
5986                     
5987                     {
5988                         tag: 'label',
5989                         'for' :  id,
5990                         cls : 'control-label col-sm-' + this.labelWidth,
5991                         html : this.fieldLabel
5992                         
5993                     },
5994                     {
5995                         cls : "col-sm-" + (12 - this.labelWidth), 
5996                         cn: [
5997                             inputblock
5998                         ]
5999                     }
6000                     
6001                 ];
6002         } else if ( this.fieldLabel.length) {
6003                 Roo.log(" label");
6004                  cfg.cn = [
6005                    
6006                     {
6007                         tag: 'label',
6008                         //cls : 'input-group-addon',
6009                         html : this.fieldLabel
6010                         
6011                     },
6012                     
6013                     inputblock
6014                     
6015                 ];
6016
6017         } else {
6018             
6019                 Roo.log(" no label && no align");
6020                 cfg.cn = [
6021                     
6022                         inputblock
6023                     
6024                 ];
6025                 
6026                 
6027         };
6028         Roo.log('input-parentType: ' + this.parentType);
6029         
6030         if (this.parentType === 'Navbar' &&  this.parent().bar) {
6031            cfg.cls += ' navbar-form';
6032            Roo.log(cfg);
6033         }
6034         
6035         return cfg;
6036         
6037     },
6038     /**
6039      * return the real input element.
6040      */
6041     inputEl: function ()
6042     {
6043         return this.el.select('input.form-control',true).first();
6044     },
6045     setDisabled : function(v)
6046     {
6047         var i  = this.inputEl().dom;
6048         if (!v) {
6049             i.removeAttribute('disabled');
6050             return;
6051             
6052         }
6053         i.setAttribute('disabled','true');
6054     },
6055     initEvents : function()
6056     {
6057         
6058         this.inputEl().on("keydown" , this.fireKey,  this);
6059         this.inputEl().on("focus", this.onFocus,  this);
6060         this.inputEl().on("blur", this.onBlur,  this);
6061         
6062         this.inputEl().relayEvent('keyup', this);
6063
6064         // reference to original value for reset
6065         this.originalValue = this.getValue();
6066         //Roo.form.TextField.superclass.initEvents.call(this);
6067         if(this.validationEvent == 'keyup'){
6068             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
6069             this.inputEl().on('keyup', this.filterValidation, this);
6070         }
6071         else if(this.validationEvent !== false){
6072             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
6073         }
6074         
6075         if(this.selectOnFocus){
6076             this.on("focus", this.preFocus, this);
6077             
6078         }
6079         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
6080             this.inputEl().on("keypress", this.filterKeys, this);
6081         }
6082        /* if(this.grow){
6083             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
6084             this.el.on("click", this.autoSize,  this);
6085         }
6086         */
6087         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
6088             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
6089         }
6090         
6091         if (typeof(this.before) == 'object') {
6092             this.before.render(this.el.select('.roo-input-before',true).first());
6093         }
6094         if (typeof(this.after) == 'object') {
6095             this.after.render(this.el.select('.roo-input-after',true).first());
6096         }
6097         
6098         
6099     },
6100     filterValidation : function(e){
6101         if(!e.isNavKeyPress()){
6102             this.validationTask.delay(this.validationDelay);
6103         }
6104     },
6105      /**
6106      * Validates the field value
6107      * @return {Boolean} True if the value is valid, else false
6108      */
6109     validate : function(){
6110         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
6111         if(this.disabled || this.validateValue(this.getRawValue())){
6112             this.clearInvalid();
6113             return true;
6114         }
6115         return false;
6116     },
6117     
6118     
6119     /**
6120      * Validates a value according to the field's validation rules and marks the field as invalid
6121      * if the validation fails
6122      * @param {Mixed} value The value to validate
6123      * @return {Boolean} True if the value is valid, else false
6124      */
6125     validateValue : function(value){
6126         if(value.length < 1)  { // if it's blank
6127              if(this.allowBlank){
6128                 this.clearInvalid();
6129                 return true;
6130              }else{
6131                 this.markInvalid(this.blankText);
6132                 return false;
6133              }
6134         }
6135         if(value.length < this.minLength){
6136             this.markInvalid(String.format(this.minLengthText, this.minLength));
6137             return false;
6138         }
6139         if(value.length > this.maxLength){
6140             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
6141             return false;
6142         }
6143         if(this.vtype){
6144             var vt = Roo.form.VTypes;
6145             if(!vt[this.vtype](value, this)){
6146                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
6147                 return false;
6148             }
6149         }
6150         if(typeof this.validator == "function"){
6151             var msg = this.validator(value);
6152             if(msg !== true){
6153                 this.markInvalid(msg);
6154                 return false;
6155             }
6156         }
6157         if(this.regex && !this.regex.test(value)){
6158             this.markInvalid(this.regexText);
6159             return false;
6160         }
6161         return true;
6162     },
6163
6164     
6165     
6166      // private
6167     fireKey : function(e){
6168         //Roo.log('field ' + e.getKey());
6169         if(e.isNavKeyPress()){
6170             this.fireEvent("specialkey", this, e);
6171         }
6172     },
6173     focus : function (selectText){
6174         if(this.rendered){
6175             this.inputEl().focus();
6176             if(selectText === true){
6177                 this.inputEl().dom.select();
6178             }
6179         }
6180         return this;
6181     } ,
6182     
6183     onFocus : function(){
6184         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
6185            // this.el.addClass(this.focusClass);
6186         }
6187         if(!this.hasFocus){
6188             this.hasFocus = true;
6189             this.startValue = this.getValue();
6190             this.fireEvent("focus", this);
6191         }
6192     },
6193     
6194     beforeBlur : Roo.emptyFn,
6195
6196     
6197     // private
6198     onBlur : function(){
6199         this.beforeBlur();
6200         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
6201             //this.el.removeClass(this.focusClass);
6202         }
6203         this.hasFocus = false;
6204         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
6205             this.validate();
6206         }
6207         var v = this.getValue();
6208         if(String(v) !== String(this.startValue)){
6209             this.fireEvent('change', this, v, this.startValue);
6210         }
6211         this.fireEvent("blur", this);
6212     },
6213     
6214     /**
6215      * Resets the current field value to the originally loaded value and clears any validation messages
6216      */
6217     reset : function(){
6218         this.setValue(this.originalValue);
6219         this.clearInvalid();
6220     },
6221      /**
6222      * Returns the name of the field
6223      * @return {Mixed} name The name field
6224      */
6225     getName: function(){
6226         return this.name;
6227     },
6228      /**
6229      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
6230      * @return {Mixed} value The field value
6231      */
6232     getValue : function(){
6233         return this.inputEl().getValue();
6234     },
6235     /**
6236      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
6237      * @return {Mixed} value The field value
6238      */
6239     getRawValue : function(){
6240         var v = this.inputEl().getValue();
6241         
6242         return v;
6243     },
6244     
6245     /**
6246      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
6247      * @param {Mixed} value The value to set
6248      */
6249     setRawValue : function(v){
6250         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
6251     },
6252     
6253     selectText : function(start, end){
6254         var v = this.getRawValue();
6255         if(v.length > 0){
6256             start = start === undefined ? 0 : start;
6257             end = end === undefined ? v.length : end;
6258             var d = this.inputEl().dom;
6259             if(d.setSelectionRange){
6260                 d.setSelectionRange(start, end);
6261             }else if(d.createTextRange){
6262                 var range = d.createTextRange();
6263                 range.moveStart("character", start);
6264                 range.moveEnd("character", v.length-end);
6265                 range.select();
6266             }
6267         }
6268     },
6269     
6270     /**
6271      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
6272      * @param {Mixed} value The value to set
6273      */
6274     setValue : function(v){
6275         this.value = v;
6276         if(this.rendered){
6277             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
6278             this.validate();
6279         }
6280     },
6281     
6282     /*
6283     processValue : function(value){
6284         if(this.stripCharsRe){
6285             var newValue = value.replace(this.stripCharsRe, '');
6286             if(newValue !== value){
6287                 this.setRawValue(newValue);
6288                 return newValue;
6289             }
6290         }
6291         return value;
6292     },
6293   */
6294     preFocus : function(){
6295         
6296         if(this.selectOnFocus){
6297             this.inputEl().dom.select();
6298         }
6299     },
6300     filterKeys : function(e){
6301         var k = e.getKey();
6302         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
6303             return;
6304         }
6305         var c = e.getCharCode(), cc = String.fromCharCode(c);
6306         if(Roo.isIE && (e.isSpecialKey() || !cc)){
6307             return;
6308         }
6309         if(!this.maskRe.test(cc)){
6310             e.stopEvent();
6311         }
6312     },
6313      /**
6314      * Clear any invalid styles/messages for this field
6315      */
6316     clearInvalid : function(){
6317         
6318         if(!this.el || this.preventMark){ // not rendered
6319             return;
6320         }
6321         this.el.removeClass(this.invalidClass);
6322         /*
6323         switch(this.msgTarget){
6324             case 'qtip':
6325                 this.el.dom.qtip = '';
6326                 break;
6327             case 'title':
6328                 this.el.dom.title = '';
6329                 break;
6330             case 'under':
6331                 if(this.errorEl){
6332                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
6333                 }
6334                 break;
6335             case 'side':
6336                 if(this.errorIcon){
6337                     this.errorIcon.dom.qtip = '';
6338                     this.errorIcon.hide();
6339                     this.un('resize', this.alignErrorIcon, this);
6340                 }
6341                 break;
6342             default:
6343                 var t = Roo.getDom(this.msgTarget);
6344                 t.innerHTML = '';
6345                 t.style.display = 'none';
6346                 break;
6347         }
6348         */
6349         this.fireEvent('valid', this);
6350     },
6351      /**
6352      * Mark this field as invalid
6353      * @param {String} msg The validation message
6354      */
6355     markInvalid : function(msg){
6356         if(!this.el  || this.preventMark){ // not rendered
6357             return;
6358         }
6359         this.el.addClass(this.invalidClass);
6360         /*
6361         msg = msg || this.invalidText;
6362         switch(this.msgTarget){
6363             case 'qtip':
6364                 this.el.dom.qtip = msg;
6365                 this.el.dom.qclass = 'x-form-invalid-tip';
6366                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
6367                     Roo.QuickTips.enable();
6368                 }
6369                 break;
6370             case 'title':
6371                 this.el.dom.title = msg;
6372                 break;
6373             case 'under':
6374                 if(!this.errorEl){
6375                     var elp = this.el.findParent('.x-form-element', 5, true);
6376                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
6377                     this.errorEl.setWidth(elp.getWidth(true)-20);
6378                 }
6379                 this.errorEl.update(msg);
6380                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
6381                 break;
6382             case 'side':
6383                 if(!this.errorIcon){
6384                     var elp = this.el.findParent('.x-form-element', 5, true);
6385                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
6386                 }
6387                 this.alignErrorIcon();
6388                 this.errorIcon.dom.qtip = msg;
6389                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
6390                 this.errorIcon.show();
6391                 this.on('resize', this.alignErrorIcon, this);
6392                 break;
6393             default:
6394                 var t = Roo.getDom(this.msgTarget);
6395                 t.innerHTML = msg;
6396                 t.style.display = this.msgDisplay;
6397                 break;
6398         }
6399         */
6400         this.fireEvent('invalid', this, msg);
6401     },
6402     // private
6403     SafariOnKeyDown : function(event)
6404     {
6405         // this is a workaround for a password hang bug on chrome/ webkit.
6406         
6407         var isSelectAll = false;
6408         
6409         if(this.inputEl().dom.selectionEnd > 0){
6410             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
6411         }
6412         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
6413             event.preventDefault();
6414             this.setValue('');
6415             return;
6416         }
6417         
6418         if(isSelectAll){ // backspace and delete key
6419             
6420             event.preventDefault();
6421             // this is very hacky as keydown always get's upper case.
6422             //
6423             var cc = String.fromCharCode(event.getCharCode());
6424             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
6425             
6426         }
6427     },
6428     adjustWidth : function(tag, w){
6429         tag = tag.toLowerCase();
6430         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
6431             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
6432                 if(tag == 'input'){
6433                     return w + 2;
6434                 }
6435                 if(tag == 'textarea'){
6436                     return w-2;
6437                 }
6438             }else if(Roo.isOpera){
6439                 if(tag == 'input'){
6440                     return w + 2;
6441                 }
6442                 if(tag == 'textarea'){
6443                     return w-2;
6444                 }
6445             }
6446         }
6447         return w;
6448     }
6449     
6450 });
6451
6452  
6453 /*
6454  * - LGPL
6455  *
6456  * Input
6457  * 
6458  */
6459
6460 /**
6461  * @class Roo.bootstrap.TextArea
6462  * @extends Roo.bootstrap.Input
6463  * Bootstrap TextArea class
6464  * @cfg {Number} cols Specifies the visible width of a text area
6465  * @cfg {Number} rows Specifies the visible number of lines in a text area
6466  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
6467  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
6468  * @cfg {string} html text
6469  * 
6470  * @constructor
6471  * Create a new TextArea
6472  * @param {Object} config The config object
6473  */
6474
6475 Roo.bootstrap.TextArea = function(config){
6476     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
6477    
6478 };
6479
6480 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
6481      
6482     cols : false,
6483     rows : 5,
6484     readOnly : false,
6485     warp : 'soft',
6486     resize : false,
6487     value: false,
6488     html: false,
6489     
6490     getAutoCreate : function(){
6491         
6492         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
6493         
6494         var id = Roo.id();
6495         
6496         var cfg = {};
6497         
6498         var input =  {
6499             tag: 'textarea',
6500             id : id,
6501             warp : this.warp,
6502             rows : this.rows,
6503             value : this.value || '',
6504             html: this.html || '',
6505             cls : 'form-control',
6506             placeholder : this.placeholder || '' 
6507             
6508         };
6509         
6510         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
6511             input.maxLength = this.maxLength;
6512         }
6513         
6514         if(this.resize){
6515             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
6516         }
6517         
6518         if(this.cols){
6519             input.cols = this.cols;
6520         }
6521         
6522         if (this.readOnly) {
6523             input.readonly = true;
6524         }
6525         
6526         if (this.name) {
6527             input.name = this.name;
6528         }
6529         
6530         if (this.size) {
6531             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
6532         }
6533         
6534         var settings=this;
6535         ['xs','sm','md','lg'].map(function(size){
6536             if (settings[size]) {
6537                 cfg.cls += ' col-' + size + '-' + settings[size];
6538             }
6539         });
6540         
6541         var inputblock = input;
6542         
6543         if (this.before || this.after) {
6544             
6545             inputblock = {
6546                 cls : 'input-group',
6547                 cn :  [] 
6548             };
6549             if (this.before) {
6550                 inputblock.cn.push({
6551                     tag :'span',
6552                     cls : 'input-group-addon',
6553                     html : this.before
6554                 });
6555             }
6556             inputblock.cn.push(input);
6557             if (this.after) {
6558                 inputblock.cn.push({
6559                     tag :'span',
6560                     cls : 'input-group-addon',
6561                     html : this.after
6562                 });
6563             }
6564             
6565         }
6566         
6567         if (align ==='left' && this.fieldLabel.length) {
6568                 Roo.log("left and has label");
6569                 cfg.cn = [
6570                     
6571                     {
6572                         tag: 'label',
6573                         'for' :  id,
6574                         cls : 'control-label col-sm-' + this.labelWidth,
6575                         html : this.fieldLabel
6576                         
6577                     },
6578                     {
6579                         cls : "col-sm-" + (12 - this.labelWidth), 
6580                         cn: [
6581                             inputblock
6582                         ]
6583                     }
6584                     
6585                 ];
6586         } else if ( this.fieldLabel.length) {
6587                 Roo.log(" label");
6588                  cfg.cn = [
6589                    
6590                     {
6591                         tag: 'label',
6592                         //cls : 'input-group-addon',
6593                         html : this.fieldLabel
6594                         
6595                     },
6596                     
6597                     inputblock
6598                     
6599                 ];
6600
6601         } else {
6602             
6603                    Roo.log(" no label && no align");
6604                 cfg.cn = [
6605                     
6606                         inputblock
6607                     
6608                 ];
6609                 
6610                 
6611         }
6612         
6613         if (this.disabled) {
6614             input.disabled=true;
6615         }
6616         
6617         return cfg;
6618         
6619     },
6620     /**
6621      * return the real textarea element.
6622      */
6623     inputEl: function ()
6624     {
6625         return this.el.select('textarea.form-control',true).first();
6626     }
6627 });
6628
6629  
6630 /*
6631  * - LGPL
6632  *
6633  * trigger field - base class for combo..
6634  * 
6635  */
6636  
6637 /**
6638  * @class Roo.bootstrap.TriggerField
6639  * @extends Roo.bootstrap.Input
6640  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
6641  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
6642  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
6643  * for which you can provide a custom implementation.  For example:
6644  * <pre><code>
6645 var trigger = new Roo.bootstrap.TriggerField();
6646 trigger.onTriggerClick = myTriggerFn;
6647 trigger.applyTo('my-field');
6648 </code></pre>
6649  *
6650  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
6651  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
6652  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
6653  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
6654  * @constructor
6655  * Create a new TriggerField.
6656  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
6657  * to the base TextField)
6658  */
6659 Roo.bootstrap.TriggerField = function(config){
6660     this.mimicing = false;
6661     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
6662 };
6663
6664 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
6665     /**
6666      * @cfg {String} triggerClass A CSS class to apply to the trigger
6667      */
6668      /**
6669      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
6670      */
6671     hideTrigger:false,
6672
6673     /** @cfg {Boolean} grow @hide */
6674     /** @cfg {Number} growMin @hide */
6675     /** @cfg {Number} growMax @hide */
6676
6677     /**
6678      * @hide 
6679      * @method
6680      */
6681     autoSize: Roo.emptyFn,
6682     // private
6683     monitorTab : true,
6684     // private
6685     deferHeight : true,
6686
6687     
6688     actionMode : 'wrap',
6689     
6690     
6691     
6692     getAutoCreate : function(){
6693        
6694         var parent = this.parent();
6695         
6696         var align = this.labelAlign || this.parentLabelAlign();
6697         
6698         var id = Roo.id();
6699         
6700         var cfg = {
6701             cls: 'form-group' //input-group
6702         };
6703         
6704         
6705         var input =  {
6706             tag: 'input',
6707             id : id,
6708             type : this.inputType,
6709             cls : 'form-control',
6710             autocomplete: 'off',
6711             placeholder : this.placeholder || '' 
6712             
6713         };
6714         if (this.name) {
6715             input.name = this.name;
6716         }
6717         if (this.size) {
6718             input.cls += ' input-' + this.size;
6719         }
6720         
6721         if (this.disabled) {
6722             input.disabled=true;
6723         }
6724         
6725         var inputblock = input;
6726         
6727         if (this.before || this.after) {
6728             
6729             inputblock = {
6730                 cls : 'input-group',
6731                 cn :  [] 
6732             };
6733             if (this.before) {
6734                 inputblock.cn.push({
6735                     tag :'span',
6736                     cls : 'input-group-addon',
6737                     html : this.before
6738                 });
6739             }
6740             inputblock.cn.push(input);
6741             if (this.after) {
6742                 inputblock.cn.push({
6743                     tag :'span',
6744                     cls : 'input-group-addon',
6745                     html : this.after
6746                 });
6747             }
6748             
6749         };
6750         
6751         var box = {
6752             tag: 'div',
6753             cn: [
6754                 {
6755                     tag: 'input',
6756                     type : 'hidden',
6757                     cls: 'form-hidden-field'
6758                 },
6759                 inputblock
6760             ]
6761             
6762         };
6763         
6764         if(this.multiple){
6765             Roo.log('multiple');
6766             
6767             box = {
6768                 tag: 'div',
6769                 cn: [
6770                     {
6771                         tag: 'input',
6772                         type : 'hidden',
6773                         cls: 'form-hidden-field'
6774                     },
6775                     {
6776                         tag: 'ul',
6777                         cls: 'select2-choices',
6778                         cn:[
6779                             {
6780                                 tag: 'li',
6781                                 cls: 'select2-search-field',
6782                                 cn: [
6783
6784                                     inputblock
6785                                 ]
6786                             }
6787                         ]
6788                     }
6789                 ]
6790             }
6791         };
6792         
6793         var combobox = {
6794             cls: 'select2-container input-group',
6795             cn: [
6796                 box,
6797                 {
6798                     tag: 'ul',
6799                     cls: 'typeahead typeahead-long dropdown-menu',
6800                     style: 'display:none'
6801                 }
6802             ]
6803         };
6804         
6805         if(!this.multiple){
6806             combobox.cn.push({
6807                 tag :'span',
6808                 cls : 'input-group-addon btn dropdown-toggle',
6809                 cn : [
6810                     {
6811                         tag: 'span',
6812                         cls: 'caret'
6813                     },
6814                     {
6815                         tag: 'span',
6816                         cls: 'combobox-clear',
6817                         cn  : [
6818                             {
6819                                 tag : 'i',
6820                                 cls: 'icon-remove'
6821                             }
6822                         ]
6823                     }
6824                 ]
6825
6826             })
6827         }
6828         
6829         if(this.multiple){
6830             combobox.cls += ' select2-container-multi';
6831         }
6832         
6833         if (align ==='left' && this.fieldLabel.length) {
6834             
6835                 Roo.log("left and has label");
6836                 cfg.cn = [
6837                     
6838                     {
6839                         tag: 'label',
6840                         'for' :  id,
6841                         cls : 'control-label col-sm-' + this.labelWidth,
6842                         html : this.fieldLabel
6843                         
6844                     },
6845                     {
6846                         cls : "col-sm-" + (12 - this.labelWidth), 
6847                         cn: [
6848                             combobox
6849                         ]
6850                     }
6851                     
6852                 ];
6853         } else if ( this.fieldLabel.length) {
6854                 Roo.log(" label");
6855                  cfg.cn = [
6856                    
6857                     {
6858                         tag: 'label',
6859                         //cls : 'input-group-addon',
6860                         html : this.fieldLabel
6861                         
6862                     },
6863                     
6864                     combobox
6865                     
6866                 ];
6867
6868         } else {
6869             
6870                 Roo.log(" no label && no align");
6871                 cfg = combobox
6872                      
6873                 
6874         }
6875          
6876         var settings=this;
6877         ['xs','sm','md','lg'].map(function(size){
6878             if (settings[size]) {
6879                 cfg.cls += ' col-' + size + '-' + settings[size];
6880             }
6881         });
6882         
6883         return cfg;
6884         
6885     },
6886     
6887     
6888     
6889     // private
6890     onResize : function(w, h){
6891 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
6892 //        if(typeof w == 'number'){
6893 //            var x = w - this.trigger.getWidth();
6894 //            this.inputEl().setWidth(this.adjustWidth('input', x));
6895 //            this.trigger.setStyle('left', x+'px');
6896 //        }
6897     },
6898
6899     // private
6900     adjustSize : Roo.BoxComponent.prototype.adjustSize,
6901
6902     // private
6903     getResizeEl : function(){
6904         return this.inputEl();
6905     },
6906
6907     // private
6908     getPositionEl : function(){
6909         return this.inputEl();
6910     },
6911
6912     // private
6913     alignErrorIcon : function(){
6914         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
6915     },
6916
6917     // private
6918     initEvents : function(){
6919         
6920         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
6921         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
6922         if(!this.multiple){
6923             this.trigger = this.el.select('span.dropdown-toggle',true).first();
6924             if(this.hideTrigger){
6925                 this.trigger.setDisplayed(false);
6926             }
6927             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
6928         }
6929         
6930         if(this.multiple){
6931             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
6932         }
6933         
6934         //this.trigger.addClassOnOver('x-form-trigger-over');
6935         //this.trigger.addClassOnClick('x-form-trigger-click');
6936         
6937         //if(!this.width){
6938         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
6939         //}
6940     },
6941
6942     // private
6943     initTrigger : function(){
6944        
6945     },
6946
6947     // private
6948     onDestroy : function(){
6949         if(this.trigger){
6950             this.trigger.removeAllListeners();
6951           //  this.trigger.remove();
6952         }
6953         //if(this.wrap){
6954         //    this.wrap.remove();
6955         //}
6956         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
6957     },
6958
6959     // private
6960     onFocus : function(){
6961         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
6962         /*
6963         if(!this.mimicing){
6964             this.wrap.addClass('x-trigger-wrap-focus');
6965             this.mimicing = true;
6966             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
6967             if(this.monitorTab){
6968                 this.el.on("keydown", this.checkTab, this);
6969             }
6970         }
6971         */
6972     },
6973
6974     // private
6975     checkTab : function(e){
6976         if(e.getKey() == e.TAB){
6977             this.triggerBlur();
6978         }
6979     },
6980
6981     // private
6982     onBlur : function(){
6983         // do nothing
6984     },
6985
6986     // private
6987     mimicBlur : function(e, t){
6988         /*
6989         if(!this.wrap.contains(t) && this.validateBlur()){
6990             this.triggerBlur();
6991         }
6992         */
6993     },
6994
6995     // private
6996     triggerBlur : function(){
6997         this.mimicing = false;
6998         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
6999         if(this.monitorTab){
7000             this.el.un("keydown", this.checkTab, this);
7001         }
7002         //this.wrap.removeClass('x-trigger-wrap-focus');
7003         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
7004     },
7005
7006     // private
7007     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
7008     validateBlur : function(e, t){
7009         return true;
7010     },
7011
7012     // private
7013     onDisable : function(){
7014         this.inputEl().dom.disabled = true;
7015         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
7016         //if(this.wrap){
7017         //    this.wrap.addClass('x-item-disabled');
7018         //}
7019     },
7020
7021     // private
7022     onEnable : function(){
7023         this.inputEl().dom.disabled = false;
7024         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
7025         //if(this.wrap){
7026         //    this.el.removeClass('x-item-disabled');
7027         //}
7028     },
7029
7030     // private
7031     onShow : function(){
7032         var ae = this.getActionEl();
7033         
7034         if(ae){
7035             ae.dom.style.display = '';
7036             ae.dom.style.visibility = 'visible';
7037         }
7038     },
7039
7040     // private
7041     
7042     onHide : function(){
7043         var ae = this.getActionEl();
7044         ae.dom.style.display = 'none';
7045     },
7046
7047     /**
7048      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
7049      * by an implementing function.
7050      * @method
7051      * @param {EventObject} e
7052      */
7053     onTriggerClick : Roo.emptyFn
7054 });
7055  /*
7056  * Based on:
7057  * Ext JS Library 1.1.1
7058  * Copyright(c) 2006-2007, Ext JS, LLC.
7059  *
7060  * Originally Released Under LGPL - original licence link has changed is not relivant.
7061  *
7062  * Fork - LGPL
7063  * <script type="text/javascript">
7064  */
7065
7066
7067 /**
7068  * @class Roo.data.SortTypes
7069  * @singleton
7070  * Defines the default sorting (casting?) comparison functions used when sorting data.
7071  */
7072 Roo.data.SortTypes = {
7073     /**
7074      * Default sort that does nothing
7075      * @param {Mixed} s The value being converted
7076      * @return {Mixed} The comparison value
7077      */
7078     none : function(s){
7079         return s;
7080     },
7081     
7082     /**
7083      * The regular expression used to strip tags
7084      * @type {RegExp}
7085      * @property
7086      */
7087     stripTagsRE : /<\/?[^>]+>/gi,
7088     
7089     /**
7090      * Strips all HTML tags to sort on text only
7091      * @param {Mixed} s The value being converted
7092      * @return {String} The comparison value
7093      */
7094     asText : function(s){
7095         return String(s).replace(this.stripTagsRE, "");
7096     },
7097     
7098     /**
7099      * Strips all HTML tags to sort on text only - Case insensitive
7100      * @param {Mixed} s The value being converted
7101      * @return {String} The comparison value
7102      */
7103     asUCText : function(s){
7104         return String(s).toUpperCase().replace(this.stripTagsRE, "");
7105     },
7106     
7107     /**
7108      * Case insensitive string
7109      * @param {Mixed} s The value being converted
7110      * @return {String} The comparison value
7111      */
7112     asUCString : function(s) {
7113         return String(s).toUpperCase();
7114     },
7115     
7116     /**
7117      * Date sorting
7118      * @param {Mixed} s The value being converted
7119      * @return {Number} The comparison value
7120      */
7121     asDate : function(s) {
7122         if(!s){
7123             return 0;
7124         }
7125         if(s instanceof Date){
7126             return s.getTime();
7127         }
7128         return Date.parse(String(s));
7129     },
7130     
7131     /**
7132      * Float sorting
7133      * @param {Mixed} s The value being converted
7134      * @return {Float} The comparison value
7135      */
7136     asFloat : function(s) {
7137         var val = parseFloat(String(s).replace(/,/g, ""));
7138         if(isNaN(val)) val = 0;
7139         return val;
7140     },
7141     
7142     /**
7143      * Integer sorting
7144      * @param {Mixed} s The value being converted
7145      * @return {Number} The comparison value
7146      */
7147     asInt : function(s) {
7148         var val = parseInt(String(s).replace(/,/g, ""));
7149         if(isNaN(val)) val = 0;
7150         return val;
7151     }
7152 };/*
7153  * Based on:
7154  * Ext JS Library 1.1.1
7155  * Copyright(c) 2006-2007, Ext JS, LLC.
7156  *
7157  * Originally Released Under LGPL - original licence link has changed is not relivant.
7158  *
7159  * Fork - LGPL
7160  * <script type="text/javascript">
7161  */
7162
7163 /**
7164 * @class Roo.data.Record
7165  * Instances of this class encapsulate both record <em>definition</em> information, and record
7166  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
7167  * to access Records cached in an {@link Roo.data.Store} object.<br>
7168  * <p>
7169  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
7170  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
7171  * objects.<br>
7172  * <p>
7173  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
7174  * @constructor
7175  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
7176  * {@link #create}. The parameters are the same.
7177  * @param {Array} data An associative Array of data values keyed by the field name.
7178  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
7179  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
7180  * not specified an integer id is generated.
7181  */
7182 Roo.data.Record = function(data, id){
7183     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
7184     this.data = data;
7185 };
7186
7187 /**
7188  * Generate a constructor for a specific record layout.
7189  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
7190  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
7191  * Each field definition object may contain the following properties: <ul>
7192  * <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,
7193  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
7194  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
7195  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
7196  * is being used, then this is a string containing the javascript expression to reference the data relative to 
7197  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
7198  * to the data item relative to the record element. If the mapping expression is the same as the field name,
7199  * this may be omitted.</p></li>
7200  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
7201  * <ul><li>auto (Default, implies no conversion)</li>
7202  * <li>string</li>
7203  * <li>int</li>
7204  * <li>float</li>
7205  * <li>boolean</li>
7206  * <li>date</li></ul></p></li>
7207  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
7208  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
7209  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
7210  * by the Reader into an object that will be stored in the Record. It is passed the
7211  * following parameters:<ul>
7212  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
7213  * </ul></p></li>
7214  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
7215  * </ul>
7216  * <br>usage:<br><pre><code>
7217 var TopicRecord = Roo.data.Record.create(
7218     {name: 'title', mapping: 'topic_title'},
7219     {name: 'author', mapping: 'username'},
7220     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
7221     {name: 'lastPost', mapping: 'post_time', type: 'date'},
7222     {name: 'lastPoster', mapping: 'user2'},
7223     {name: 'excerpt', mapping: 'post_text'}
7224 );
7225
7226 var myNewRecord = new TopicRecord({
7227     title: 'Do my job please',
7228     author: 'noobie',
7229     totalPosts: 1,
7230     lastPost: new Date(),
7231     lastPoster: 'Animal',
7232     excerpt: 'No way dude!'
7233 });
7234 myStore.add(myNewRecord);
7235 </code></pre>
7236  * @method create
7237  * @static
7238  */
7239 Roo.data.Record.create = function(o){
7240     var f = function(){
7241         f.superclass.constructor.apply(this, arguments);
7242     };
7243     Roo.extend(f, Roo.data.Record);
7244     var p = f.prototype;
7245     p.fields = new Roo.util.MixedCollection(false, function(field){
7246         return field.name;
7247     });
7248     for(var i = 0, len = o.length; i < len; i++){
7249         p.fields.add(new Roo.data.Field(o[i]));
7250     }
7251     f.getField = function(name){
7252         return p.fields.get(name);  
7253     };
7254     return f;
7255 };
7256
7257 Roo.data.Record.AUTO_ID = 1000;
7258 Roo.data.Record.EDIT = 'edit';
7259 Roo.data.Record.REJECT = 'reject';
7260 Roo.data.Record.COMMIT = 'commit';
7261
7262 Roo.data.Record.prototype = {
7263     /**
7264      * Readonly flag - true if this record has been modified.
7265      * @type Boolean
7266      */
7267     dirty : false,
7268     editing : false,
7269     error: null,
7270     modified: null,
7271
7272     // private
7273     join : function(store){
7274         this.store = store;
7275     },
7276
7277     /**
7278      * Set the named field to the specified value.
7279      * @param {String} name The name of the field to set.
7280      * @param {Object} value The value to set the field to.
7281      */
7282     set : function(name, value){
7283         if(this.data[name] == value){
7284             return;
7285         }
7286         this.dirty = true;
7287         if(!this.modified){
7288             this.modified = {};
7289         }
7290         if(typeof this.modified[name] == 'undefined'){
7291             this.modified[name] = this.data[name];
7292         }
7293         this.data[name] = value;
7294         if(!this.editing && this.store){
7295             this.store.afterEdit(this);
7296         }       
7297     },
7298
7299     /**
7300      * Get the value of the named field.
7301      * @param {String} name The name of the field to get the value of.
7302      * @return {Object} The value of the field.
7303      */
7304     get : function(name){
7305         return this.data[name]; 
7306     },
7307
7308     // private
7309     beginEdit : function(){
7310         this.editing = true;
7311         this.modified = {}; 
7312     },
7313
7314     // private
7315     cancelEdit : function(){
7316         this.editing = false;
7317         delete this.modified;
7318     },
7319
7320     // private
7321     endEdit : function(){
7322         this.editing = false;
7323         if(this.dirty && this.store){
7324             this.store.afterEdit(this);
7325         }
7326     },
7327
7328     /**
7329      * Usually called by the {@link Roo.data.Store} which owns the Record.
7330      * Rejects all changes made to the Record since either creation, or the last commit operation.
7331      * Modified fields are reverted to their original values.
7332      * <p>
7333      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
7334      * of reject operations.
7335      */
7336     reject : function(){
7337         var m = this.modified;
7338         for(var n in m){
7339             if(typeof m[n] != "function"){
7340                 this.data[n] = m[n];
7341             }
7342         }
7343         this.dirty = false;
7344         delete this.modified;
7345         this.editing = false;
7346         if(this.store){
7347             this.store.afterReject(this);
7348         }
7349     },
7350
7351     /**
7352      * Usually called by the {@link Roo.data.Store} which owns the Record.
7353      * Commits all changes made to the Record since either creation, or the last commit operation.
7354      * <p>
7355      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
7356      * of commit operations.
7357      */
7358     commit : function(){
7359         this.dirty = false;
7360         delete this.modified;
7361         this.editing = false;
7362         if(this.store){
7363             this.store.afterCommit(this);
7364         }
7365     },
7366
7367     // private
7368     hasError : function(){
7369         return this.error != null;
7370     },
7371
7372     // private
7373     clearError : function(){
7374         this.error = null;
7375     },
7376
7377     /**
7378      * Creates a copy of this record.
7379      * @param {String} id (optional) A new record id if you don't want to use this record's id
7380      * @return {Record}
7381      */
7382     copy : function(newId) {
7383         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
7384     }
7385 };/*
7386  * Based on:
7387  * Ext JS Library 1.1.1
7388  * Copyright(c) 2006-2007, Ext JS, LLC.
7389  *
7390  * Originally Released Under LGPL - original licence link has changed is not relivant.
7391  *
7392  * Fork - LGPL
7393  * <script type="text/javascript">
7394  */
7395
7396
7397
7398 /**
7399  * @class Roo.data.Store
7400  * @extends Roo.util.Observable
7401  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
7402  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
7403  * <p>
7404  * 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
7405  * has no knowledge of the format of the data returned by the Proxy.<br>
7406  * <p>
7407  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
7408  * instances from the data object. These records are cached and made available through accessor functions.
7409  * @constructor
7410  * Creates a new Store.
7411  * @param {Object} config A config object containing the objects needed for the Store to access data,
7412  * and read the data into Records.
7413  */
7414 Roo.data.Store = function(config){
7415     this.data = new Roo.util.MixedCollection(false);
7416     this.data.getKey = function(o){
7417         return o.id;
7418     };
7419     this.baseParams = {};
7420     // private
7421     this.paramNames = {
7422         "start" : "start",
7423         "limit" : "limit",
7424         "sort" : "sort",
7425         "dir" : "dir",
7426         "multisort" : "_multisort"
7427     };
7428
7429     if(config && config.data){
7430         this.inlineData = config.data;
7431         delete config.data;
7432     }
7433
7434     Roo.apply(this, config);
7435     
7436     if(this.reader){ // reader passed
7437         this.reader = Roo.factory(this.reader, Roo.data);
7438         this.reader.xmodule = this.xmodule || false;
7439         if(!this.recordType){
7440             this.recordType = this.reader.recordType;
7441         }
7442         if(this.reader.onMetaChange){
7443             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
7444         }
7445     }
7446
7447     if(this.recordType){
7448         this.fields = this.recordType.prototype.fields;
7449     }
7450     this.modified = [];
7451
7452     this.addEvents({
7453         /**
7454          * @event datachanged
7455          * Fires when the data cache has changed, and a widget which is using this Store
7456          * as a Record cache should refresh its view.
7457          * @param {Store} this
7458          */
7459         datachanged : true,
7460         /**
7461          * @event metachange
7462          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
7463          * @param {Store} this
7464          * @param {Object} meta The JSON metadata
7465          */
7466         metachange : true,
7467         /**
7468          * @event add
7469          * Fires when Records have been added to the Store
7470          * @param {Store} this
7471          * @param {Roo.data.Record[]} records The array of Records added
7472          * @param {Number} index The index at which the record(s) were added
7473          */
7474         add : true,
7475         /**
7476          * @event remove
7477          * Fires when a Record has been removed from the Store
7478          * @param {Store} this
7479          * @param {Roo.data.Record} record The Record that was removed
7480          * @param {Number} index The index at which the record was removed
7481          */
7482         remove : true,
7483         /**
7484          * @event update
7485          * Fires when a Record has been updated
7486          * @param {Store} this
7487          * @param {Roo.data.Record} record The Record that was updated
7488          * @param {String} operation The update operation being performed.  Value may be one of:
7489          * <pre><code>
7490  Roo.data.Record.EDIT
7491  Roo.data.Record.REJECT
7492  Roo.data.Record.COMMIT
7493          * </code></pre>
7494          */
7495         update : true,
7496         /**
7497          * @event clear
7498          * Fires when the data cache has been cleared.
7499          * @param {Store} this
7500          */
7501         clear : true,
7502         /**
7503          * @event beforeload
7504          * Fires before a request is made for a new data object.  If the beforeload handler returns false
7505          * the load action will be canceled.
7506          * @param {Store} this
7507          * @param {Object} options The loading options that were specified (see {@link #load} for details)
7508          */
7509         beforeload : true,
7510         /**
7511          * @event beforeloadadd
7512          * Fires after a new set of Records has been loaded.
7513          * @param {Store} this
7514          * @param {Roo.data.Record[]} records The Records that were loaded
7515          * @param {Object} options The loading options that were specified (see {@link #load} for details)
7516          */
7517         beforeloadadd : true,
7518         /**
7519          * @event load
7520          * Fires after a new set of Records has been loaded, before they are added to the store.
7521          * @param {Store} this
7522          * @param {Roo.data.Record[]} records The Records that were loaded
7523          * @param {Object} options The loading options that were specified (see {@link #load} for details)
7524          * @params {Object} return from reader
7525          */
7526         load : true,
7527         /**
7528          * @event loadexception
7529          * Fires if an exception occurs in the Proxy during loading.
7530          * Called with the signature of the Proxy's "loadexception" event.
7531          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
7532          * 
7533          * @param {Proxy} 
7534          * @param {Object} return from JsonData.reader() - success, totalRecords, records
7535          * @param {Object} load options 
7536          * @param {Object} jsonData from your request (normally this contains the Exception)
7537          */
7538         loadexception : true
7539     });
7540     
7541     if(this.proxy){
7542         this.proxy = Roo.factory(this.proxy, Roo.data);
7543         this.proxy.xmodule = this.xmodule || false;
7544         this.relayEvents(this.proxy,  ["loadexception"]);
7545     }
7546     this.sortToggle = {};
7547     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
7548
7549     Roo.data.Store.superclass.constructor.call(this);
7550
7551     if(this.inlineData){
7552         this.loadData(this.inlineData);
7553         delete this.inlineData;
7554     }
7555 };
7556
7557 Roo.extend(Roo.data.Store, Roo.util.Observable, {
7558      /**
7559     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
7560     * without a remote query - used by combo/forms at present.
7561     */
7562     
7563     /**
7564     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
7565     */
7566     /**
7567     * @cfg {Array} data Inline data to be loaded when the store is initialized.
7568     */
7569     /**
7570     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
7571     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
7572     */
7573     /**
7574     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
7575     * on any HTTP request
7576     */
7577     /**
7578     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
7579     */
7580     /**
7581     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
7582     */
7583     multiSort: false,
7584     /**
7585     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
7586     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
7587     */
7588     remoteSort : false,
7589
7590     /**
7591     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
7592      * loaded or when a record is removed. (defaults to false).
7593     */
7594     pruneModifiedRecords : false,
7595
7596     // private
7597     lastOptions : null,
7598
7599     /**
7600      * Add Records to the Store and fires the add event.
7601      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
7602      */
7603     add : function(records){
7604         records = [].concat(records);
7605         for(var i = 0, len = records.length; i < len; i++){
7606             records[i].join(this);
7607         }
7608         var index = this.data.length;
7609         this.data.addAll(records);
7610         this.fireEvent("add", this, records, index);
7611     },
7612
7613     /**
7614      * Remove a Record from the Store and fires the remove event.
7615      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
7616      */
7617     remove : function(record){
7618         var index = this.data.indexOf(record);
7619         this.data.removeAt(index);
7620         if(this.pruneModifiedRecords){
7621             this.modified.remove(record);
7622         }
7623         this.fireEvent("remove", this, record, index);
7624     },
7625
7626     /**
7627      * Remove all Records from the Store and fires the clear event.
7628      */
7629     removeAll : function(){
7630         this.data.clear();
7631         if(this.pruneModifiedRecords){
7632             this.modified = [];
7633         }
7634         this.fireEvent("clear", this);
7635     },
7636
7637     /**
7638      * Inserts Records to the Store at the given index and fires the add event.
7639      * @param {Number} index The start index at which to insert the passed Records.
7640      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
7641      */
7642     insert : function(index, records){
7643         records = [].concat(records);
7644         for(var i = 0, len = records.length; i < len; i++){
7645             this.data.insert(index, records[i]);
7646             records[i].join(this);
7647         }
7648         this.fireEvent("add", this, records, index);
7649     },
7650
7651     /**
7652      * Get the index within the cache of the passed Record.
7653      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
7654      * @return {Number} The index of the passed Record. Returns -1 if not found.
7655      */
7656     indexOf : function(record){
7657         return this.data.indexOf(record);
7658     },
7659
7660     /**
7661      * Get the index within the cache of the Record with the passed id.
7662      * @param {String} id The id of the Record to find.
7663      * @return {Number} The index of the Record. Returns -1 if not found.
7664      */
7665     indexOfId : function(id){
7666         return this.data.indexOfKey(id);
7667     },
7668
7669     /**
7670      * Get the Record with the specified id.
7671      * @param {String} id The id of the Record to find.
7672      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
7673      */
7674     getById : function(id){
7675         return this.data.key(id);
7676     },
7677
7678     /**
7679      * Get the Record at the specified index.
7680      * @param {Number} index The index of the Record to find.
7681      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
7682      */
7683     getAt : function(index){
7684         return this.data.itemAt(index);
7685     },
7686
7687     /**
7688      * Returns a range of Records between specified indices.
7689      * @param {Number} startIndex (optional) The starting index (defaults to 0)
7690      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
7691      * @return {Roo.data.Record[]} An array of Records
7692      */
7693     getRange : function(start, end){
7694         return this.data.getRange(start, end);
7695     },
7696
7697     // private
7698     storeOptions : function(o){
7699         o = Roo.apply({}, o);
7700         delete o.callback;
7701         delete o.scope;
7702         this.lastOptions = o;
7703     },
7704
7705     /**
7706      * Loads the Record cache from the configured Proxy using the configured Reader.
7707      * <p>
7708      * If using remote paging, then the first load call must specify the <em>start</em>
7709      * and <em>limit</em> properties in the options.params property to establish the initial
7710      * position within the dataset, and the number of Records to cache on each read from the Proxy.
7711      * <p>
7712      * <strong>It is important to note that for remote data sources, loading is asynchronous,
7713      * and this call will return before the new data has been loaded. Perform any post-processing
7714      * in a callback function, or in a "load" event handler.</strong>
7715      * <p>
7716      * @param {Object} options An object containing properties which control loading options:<ul>
7717      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
7718      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
7719      * passed the following arguments:<ul>
7720      * <li>r : Roo.data.Record[]</li>
7721      * <li>options: Options object from the load call</li>
7722      * <li>success: Boolean success indicator</li></ul></li>
7723      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
7724      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
7725      * </ul>
7726      */
7727     load : function(options){
7728         options = options || {};
7729         if(this.fireEvent("beforeload", this, options) !== false){
7730             this.storeOptions(options);
7731             var p = Roo.apply(options.params || {}, this.baseParams);
7732             // if meta was not loaded from remote source.. try requesting it.
7733             if (!this.reader.metaFromRemote) {
7734                 p._requestMeta = 1;
7735             }
7736             if(this.sortInfo && this.remoteSort){
7737                 var pn = this.paramNames;
7738                 p[pn["sort"]] = this.sortInfo.field;
7739                 p[pn["dir"]] = this.sortInfo.direction;
7740             }
7741             if (this.multiSort) {
7742                 var pn = this.paramNames;
7743                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
7744             }
7745             
7746             this.proxy.load(p, this.reader, this.loadRecords, this, options);
7747         }
7748     },
7749
7750     /**
7751      * Reloads the Record cache from the configured Proxy using the configured Reader and
7752      * the options from the last load operation performed.
7753      * @param {Object} options (optional) An object containing properties which may override the options
7754      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
7755      * the most recently used options are reused).
7756      */
7757     reload : function(options){
7758         this.load(Roo.applyIf(options||{}, this.lastOptions));
7759     },
7760
7761     // private
7762     // Called as a callback by the Reader during a load operation.
7763     loadRecords : function(o, options, success){
7764         if(!o || success === false){
7765             if(success !== false){
7766                 this.fireEvent("load", this, [], options, o);
7767             }
7768             if(options.callback){
7769                 options.callback.call(options.scope || this, [], options, false);
7770             }
7771             return;
7772         }
7773         // if data returned failure - throw an exception.
7774         if (o.success === false) {
7775             // show a message if no listener is registered.
7776             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
7777                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
7778             }
7779             // loadmask wil be hooked into this..
7780             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
7781             return;
7782         }
7783         var r = o.records, t = o.totalRecords || r.length;
7784         
7785         this.fireEvent("beforeloadadd", this, r, options, o);
7786         
7787         if(!options || options.add !== true){
7788             if(this.pruneModifiedRecords){
7789                 this.modified = [];
7790             }
7791             for(var i = 0, len = r.length; i < len; i++){
7792                 r[i].join(this);
7793             }
7794             if(this.snapshot){
7795                 this.data = this.snapshot;
7796                 delete this.snapshot;
7797             }
7798             this.data.clear();
7799             this.data.addAll(r);
7800             this.totalLength = t;
7801             this.applySort();
7802             this.fireEvent("datachanged", this);
7803         }else{
7804             this.totalLength = Math.max(t, this.data.length+r.length);
7805             this.add(r);
7806         }
7807         this.fireEvent("load", this, r, options, o);
7808         if(options.callback){
7809             options.callback.call(options.scope || this, r, options, true);
7810         }
7811     },
7812
7813
7814     /**
7815      * Loads data from a passed data block. A Reader which understands the format of the data
7816      * must have been configured in the constructor.
7817      * @param {Object} data The data block from which to read the Records.  The format of the data expected
7818      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
7819      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
7820      */
7821     loadData : function(o, append){
7822         var r = this.reader.readRecords(o);
7823         this.loadRecords(r, {add: append}, true);
7824     },
7825
7826     /**
7827      * Gets the number of cached records.
7828      * <p>
7829      * <em>If using paging, this may not be the total size of the dataset. If the data object
7830      * used by the Reader contains the dataset size, then the getTotalCount() function returns
7831      * the data set size</em>
7832      */
7833     getCount : function(){
7834         return this.data.length || 0;
7835     },
7836
7837     /**
7838      * Gets the total number of records in the dataset as returned by the server.
7839      * <p>
7840      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
7841      * the dataset size</em>
7842      */
7843     getTotalCount : function(){
7844         return this.totalLength || 0;
7845     },
7846
7847     /**
7848      * Returns the sort state of the Store as an object with two properties:
7849      * <pre><code>
7850  field {String} The name of the field by which the Records are sorted
7851  direction {String} The sort order, "ASC" or "DESC"
7852      * </code></pre>
7853      */
7854     getSortState : function(){
7855         return this.sortInfo;
7856     },
7857
7858     // private
7859     applySort : function(){
7860         if(this.sortInfo && !this.remoteSort){
7861             var s = this.sortInfo, f = s.field;
7862             var st = this.fields.get(f).sortType;
7863             var fn = function(r1, r2){
7864                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
7865                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
7866             };
7867             this.data.sort(s.direction, fn);
7868             if(this.snapshot && this.snapshot != this.data){
7869                 this.snapshot.sort(s.direction, fn);
7870             }
7871         }
7872     },
7873
7874     /**
7875      * Sets the default sort column and order to be used by the next load operation.
7876      * @param {String} fieldName The name of the field to sort by.
7877      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
7878      */
7879     setDefaultSort : function(field, dir){
7880         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
7881     },
7882
7883     /**
7884      * Sort the Records.
7885      * If remote sorting is used, the sort is performed on the server, and the cache is
7886      * reloaded. If local sorting is used, the cache is sorted internally.
7887      * @param {String} fieldName The name of the field to sort by.
7888      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
7889      */
7890     sort : function(fieldName, dir){
7891         var f = this.fields.get(fieldName);
7892         if(!dir){
7893             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
7894             
7895             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
7896                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
7897             }else{
7898                 dir = f.sortDir;
7899             }
7900         }
7901         this.sortToggle[f.name] = dir;
7902         this.sortInfo = {field: f.name, direction: dir};
7903         if(!this.remoteSort){
7904             this.applySort();
7905             this.fireEvent("datachanged", this);
7906         }else{
7907             this.load(this.lastOptions);
7908         }
7909     },
7910
7911     /**
7912      * Calls the specified function for each of the Records in the cache.
7913      * @param {Function} fn The function to call. The Record is passed as the first parameter.
7914      * Returning <em>false</em> aborts and exits the iteration.
7915      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
7916      */
7917     each : function(fn, scope){
7918         this.data.each(fn, scope);
7919     },
7920
7921     /**
7922      * Gets all records modified since the last commit.  Modified records are persisted across load operations
7923      * (e.g., during paging).
7924      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
7925      */
7926     getModifiedRecords : function(){
7927         return this.modified;
7928     },
7929
7930     // private
7931     createFilterFn : function(property, value, anyMatch){
7932         if(!value.exec){ // not a regex
7933             value = String(value);
7934             if(value.length == 0){
7935                 return false;
7936             }
7937             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
7938         }
7939         return function(r){
7940             return value.test(r.data[property]);
7941         };
7942     },
7943
7944     /**
7945      * Sums the value of <i>property</i> for each record between start and end and returns the result.
7946      * @param {String} property A field on your records
7947      * @param {Number} start The record index to start at (defaults to 0)
7948      * @param {Number} end The last record index to include (defaults to length - 1)
7949      * @return {Number} The sum
7950      */
7951     sum : function(property, start, end){
7952         var rs = this.data.items, v = 0;
7953         start = start || 0;
7954         end = (end || end === 0) ? end : rs.length-1;
7955
7956         for(var i = start; i <= end; i++){
7957             v += (rs[i].data[property] || 0);
7958         }
7959         return v;
7960     },
7961
7962     /**
7963      * Filter the records by a specified property.
7964      * @param {String} field A field on your records
7965      * @param {String/RegExp} value Either a string that the field
7966      * should start with or a RegExp to test against the field
7967      * @param {Boolean} anyMatch True to match any part not just the beginning
7968      */
7969     filter : function(property, value, anyMatch){
7970         var fn = this.createFilterFn(property, value, anyMatch);
7971         return fn ? this.filterBy(fn) : this.clearFilter();
7972     },
7973
7974     /**
7975      * Filter by a function. The specified function will be called with each
7976      * record in this data source. If the function returns true the record is included,
7977      * otherwise it is filtered.
7978      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
7979      * @param {Object} scope (optional) The scope of the function (defaults to this)
7980      */
7981     filterBy : function(fn, scope){
7982         this.snapshot = this.snapshot || this.data;
7983         this.data = this.queryBy(fn, scope||this);
7984         this.fireEvent("datachanged", this);
7985     },
7986
7987     /**
7988      * Query the records by a specified property.
7989      * @param {String} field A field on your records
7990      * @param {String/RegExp} value Either a string that the field
7991      * should start with or a RegExp to test against the field
7992      * @param {Boolean} anyMatch True to match any part not just the beginning
7993      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
7994      */
7995     query : function(property, value, anyMatch){
7996         var fn = this.createFilterFn(property, value, anyMatch);
7997         return fn ? this.queryBy(fn) : this.data.clone();
7998     },
7999
8000     /**
8001      * Query by a function. The specified function will be called with each
8002      * record in this data source. If the function returns true the record is included
8003      * in the results.
8004      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
8005      * @param {Object} scope (optional) The scope of the function (defaults to this)
8006       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
8007      **/
8008     queryBy : function(fn, scope){
8009         var data = this.snapshot || this.data;
8010         return data.filterBy(fn, scope||this);
8011     },
8012
8013     /**
8014      * Collects unique values for a particular dataIndex from this store.
8015      * @param {String} dataIndex The property to collect
8016      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
8017      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
8018      * @return {Array} An array of the unique values
8019      **/
8020     collect : function(dataIndex, allowNull, bypassFilter){
8021         var d = (bypassFilter === true && this.snapshot) ?
8022                 this.snapshot.items : this.data.items;
8023         var v, sv, r = [], l = {};
8024         for(var i = 0, len = d.length; i < len; i++){
8025             v = d[i].data[dataIndex];
8026             sv = String(v);
8027             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
8028                 l[sv] = true;
8029                 r[r.length] = v;
8030             }
8031         }
8032         return r;
8033     },
8034
8035     /**
8036      * Revert to a view of the Record cache with no filtering applied.
8037      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
8038      */
8039     clearFilter : function(suppressEvent){
8040         if(this.snapshot && this.snapshot != this.data){
8041             this.data = this.snapshot;
8042             delete this.snapshot;
8043             if(suppressEvent !== true){
8044                 this.fireEvent("datachanged", this);
8045             }
8046         }
8047     },
8048
8049     // private
8050     afterEdit : function(record){
8051         if(this.modified.indexOf(record) == -1){
8052             this.modified.push(record);
8053         }
8054         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
8055     },
8056     
8057     // private
8058     afterReject : function(record){
8059         this.modified.remove(record);
8060         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
8061     },
8062
8063     // private
8064     afterCommit : function(record){
8065         this.modified.remove(record);
8066         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
8067     },
8068
8069     /**
8070      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
8071      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
8072      */
8073     commitChanges : function(){
8074         var m = this.modified.slice(0);
8075         this.modified = [];
8076         for(var i = 0, len = m.length; i < len; i++){
8077             m[i].commit();
8078         }
8079     },
8080
8081     /**
8082      * Cancel outstanding changes on all changed records.
8083      */
8084     rejectChanges : function(){
8085         var m = this.modified.slice(0);
8086         this.modified = [];
8087         for(var i = 0, len = m.length; i < len; i++){
8088             m[i].reject();
8089         }
8090     },
8091
8092     onMetaChange : function(meta, rtype, o){
8093         this.recordType = rtype;
8094         this.fields = rtype.prototype.fields;
8095         delete this.snapshot;
8096         this.sortInfo = meta.sortInfo || this.sortInfo;
8097         this.modified = [];
8098         this.fireEvent('metachange', this, this.reader.meta);
8099     },
8100     
8101     moveIndex : function(data, type)
8102     {
8103         var index = this.indexOf(data);
8104         
8105         var newIndex = index + type;
8106         
8107         this.remove(data);
8108         
8109         this.insert(newIndex, data);
8110         
8111     }
8112 });/*
8113  * Based on:
8114  * Ext JS Library 1.1.1
8115  * Copyright(c) 2006-2007, Ext JS, LLC.
8116  *
8117  * Originally Released Under LGPL - original licence link has changed is not relivant.
8118  *
8119  * Fork - LGPL
8120  * <script type="text/javascript">
8121  */
8122
8123 /**
8124  * @class Roo.data.SimpleStore
8125  * @extends Roo.data.Store
8126  * Small helper class to make creating Stores from Array data easier.
8127  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
8128  * @cfg {Array} fields An array of field definition objects, or field name strings.
8129  * @cfg {Array} data The multi-dimensional array of data
8130  * @constructor
8131  * @param {Object} config
8132  */
8133 Roo.data.SimpleStore = function(config){
8134     Roo.data.SimpleStore.superclass.constructor.call(this, {
8135         isLocal : true,
8136         reader: new Roo.data.ArrayReader({
8137                 id: config.id
8138             },
8139             Roo.data.Record.create(config.fields)
8140         ),
8141         proxy : new Roo.data.MemoryProxy(config.data)
8142     });
8143     this.load();
8144 };
8145 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
8146  * Based on:
8147  * Ext JS Library 1.1.1
8148  * Copyright(c) 2006-2007, Ext JS, LLC.
8149  *
8150  * Originally Released Under LGPL - original licence link has changed is not relivant.
8151  *
8152  * Fork - LGPL
8153  * <script type="text/javascript">
8154  */
8155
8156 /**
8157 /**
8158  * @extends Roo.data.Store
8159  * @class Roo.data.JsonStore
8160  * Small helper class to make creating Stores for JSON data easier. <br/>
8161 <pre><code>
8162 var store = new Roo.data.JsonStore({
8163     url: 'get-images.php',
8164     root: 'images',
8165     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
8166 });
8167 </code></pre>
8168  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
8169  * JsonReader and HttpProxy (unless inline data is provided).</b>
8170  * @cfg {Array} fields An array of field definition objects, or field name strings.
8171  * @constructor
8172  * @param {Object} config
8173  */
8174 Roo.data.JsonStore = function(c){
8175     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
8176         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
8177         reader: new Roo.data.JsonReader(c, c.fields)
8178     }));
8179 };
8180 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
8181  * Based on:
8182  * Ext JS Library 1.1.1
8183  * Copyright(c) 2006-2007, Ext JS, LLC.
8184  *
8185  * Originally Released Under LGPL - original licence link has changed is not relivant.
8186  *
8187  * Fork - LGPL
8188  * <script type="text/javascript">
8189  */
8190
8191  
8192 Roo.data.Field = function(config){
8193     if(typeof config == "string"){
8194         config = {name: config};
8195     }
8196     Roo.apply(this, config);
8197     
8198     if(!this.type){
8199         this.type = "auto";
8200     }
8201     
8202     var st = Roo.data.SortTypes;
8203     // named sortTypes are supported, here we look them up
8204     if(typeof this.sortType == "string"){
8205         this.sortType = st[this.sortType];
8206     }
8207     
8208     // set default sortType for strings and dates
8209     if(!this.sortType){
8210         switch(this.type){
8211             case "string":
8212                 this.sortType = st.asUCString;
8213                 break;
8214             case "date":
8215                 this.sortType = st.asDate;
8216                 break;
8217             default:
8218                 this.sortType = st.none;
8219         }
8220     }
8221
8222     // define once
8223     var stripRe = /[\$,%]/g;
8224
8225     // prebuilt conversion function for this field, instead of
8226     // switching every time we're reading a value
8227     if(!this.convert){
8228         var cv, dateFormat = this.dateFormat;
8229         switch(this.type){
8230             case "":
8231             case "auto":
8232             case undefined:
8233                 cv = function(v){ return v; };
8234                 break;
8235             case "string":
8236                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
8237                 break;
8238             case "int":
8239                 cv = function(v){
8240                     return v !== undefined && v !== null && v !== '' ?
8241                            parseInt(String(v).replace(stripRe, ""), 10) : '';
8242                     };
8243                 break;
8244             case "float":
8245                 cv = function(v){
8246                     return v !== undefined && v !== null && v !== '' ?
8247                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
8248                     };
8249                 break;
8250             case "bool":
8251             case "boolean":
8252                 cv = function(v){ return v === true || v === "true" || v == 1; };
8253                 break;
8254             case "date":
8255                 cv = function(v){
8256                     if(!v){
8257                         return '';
8258                     }
8259                     if(v instanceof Date){
8260                         return v;
8261                     }
8262                     if(dateFormat){
8263                         if(dateFormat == "timestamp"){
8264                             return new Date(v*1000);
8265                         }
8266                         return Date.parseDate(v, dateFormat);
8267                     }
8268                     var parsed = Date.parse(v);
8269                     return parsed ? new Date(parsed) : null;
8270                 };
8271              break;
8272             
8273         }
8274         this.convert = cv;
8275     }
8276 };
8277
8278 Roo.data.Field.prototype = {
8279     dateFormat: null,
8280     defaultValue: "",
8281     mapping: null,
8282     sortType : null,
8283     sortDir : "ASC"
8284 };/*
8285  * Based on:
8286  * Ext JS Library 1.1.1
8287  * Copyright(c) 2006-2007, Ext JS, LLC.
8288  *
8289  * Originally Released Under LGPL - original licence link has changed is not relivant.
8290  *
8291  * Fork - LGPL
8292  * <script type="text/javascript">
8293  */
8294  
8295 // Base class for reading structured data from a data source.  This class is intended to be
8296 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
8297
8298 /**
8299  * @class Roo.data.DataReader
8300  * Base class for reading structured data from a data source.  This class is intended to be
8301  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
8302  */
8303
8304 Roo.data.DataReader = function(meta, recordType){
8305     
8306     this.meta = meta;
8307     
8308     this.recordType = recordType instanceof Array ? 
8309         Roo.data.Record.create(recordType) : recordType;
8310 };
8311
8312 Roo.data.DataReader.prototype = {
8313      /**
8314      * Create an empty record
8315      * @param {Object} data (optional) - overlay some values
8316      * @return {Roo.data.Record} record created.
8317      */
8318     newRow :  function(d) {
8319         var da =  {};
8320         this.recordType.prototype.fields.each(function(c) {
8321             switch( c.type) {
8322                 case 'int' : da[c.name] = 0; break;
8323                 case 'date' : da[c.name] = new Date(); break;
8324                 case 'float' : da[c.name] = 0.0; break;
8325                 case 'boolean' : da[c.name] = false; break;
8326                 default : da[c.name] = ""; break;
8327             }
8328             
8329         });
8330         return new this.recordType(Roo.apply(da, d));
8331     }
8332     
8333 };/*
8334  * Based on:
8335  * Ext JS Library 1.1.1
8336  * Copyright(c) 2006-2007, Ext JS, LLC.
8337  *
8338  * Originally Released Under LGPL - original licence link has changed is not relivant.
8339  *
8340  * Fork - LGPL
8341  * <script type="text/javascript">
8342  */
8343
8344 /**
8345  * @class Roo.data.DataProxy
8346  * @extends Roo.data.Observable
8347  * This class is an abstract base class for implementations which provide retrieval of
8348  * unformatted data objects.<br>
8349  * <p>
8350  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
8351  * (of the appropriate type which knows how to parse the data object) to provide a block of
8352  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
8353  * <p>
8354  * Custom implementations must implement the load method as described in
8355  * {@link Roo.data.HttpProxy#load}.
8356  */
8357 Roo.data.DataProxy = function(){
8358     this.addEvents({
8359         /**
8360          * @event beforeload
8361          * Fires before a network request is made to retrieve a data object.
8362          * @param {Object} This DataProxy object.
8363          * @param {Object} params The params parameter to the load function.
8364          */
8365         beforeload : true,
8366         /**
8367          * @event load
8368          * Fires before the load method's callback is called.
8369          * @param {Object} This DataProxy object.
8370          * @param {Object} o The data object.
8371          * @param {Object} arg The callback argument object passed to the load function.
8372          */
8373         load : true,
8374         /**
8375          * @event loadexception
8376          * Fires if an Exception occurs during data retrieval.
8377          * @param {Object} This DataProxy object.
8378          * @param {Object} o The data object.
8379          * @param {Object} arg The callback argument object passed to the load function.
8380          * @param {Object} e The Exception.
8381          */
8382         loadexception : true
8383     });
8384     Roo.data.DataProxy.superclass.constructor.call(this);
8385 };
8386
8387 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
8388
8389     /**
8390      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
8391      */
8392 /*
8393  * Based on:
8394  * Ext JS Library 1.1.1
8395  * Copyright(c) 2006-2007, Ext JS, LLC.
8396  *
8397  * Originally Released Under LGPL - original licence link has changed is not relivant.
8398  *
8399  * Fork - LGPL
8400  * <script type="text/javascript">
8401  */
8402 /**
8403  * @class Roo.data.MemoryProxy
8404  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
8405  * to the Reader when its load method is called.
8406  * @constructor
8407  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
8408  */
8409 Roo.data.MemoryProxy = function(data){
8410     if (data.data) {
8411         data = data.data;
8412     }
8413     Roo.data.MemoryProxy.superclass.constructor.call(this);
8414     this.data = data;
8415 };
8416
8417 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
8418     /**
8419      * Load data from the requested source (in this case an in-memory
8420      * data object passed to the constructor), read the data object into
8421      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
8422      * process that block using the passed callback.
8423      * @param {Object} params This parameter is not used by the MemoryProxy class.
8424      * @param {Roo.data.DataReader} reader The Reader object which converts the data
8425      * object into a block of Roo.data.Records.
8426      * @param {Function} callback The function into which to pass the block of Roo.data.records.
8427      * The function must be passed <ul>
8428      * <li>The Record block object</li>
8429      * <li>The "arg" argument from the load function</li>
8430      * <li>A boolean success indicator</li>
8431      * </ul>
8432      * @param {Object} scope The scope in which to call the callback
8433      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
8434      */
8435     load : function(params, reader, callback, scope, arg){
8436         params = params || {};
8437         var result;
8438         try {
8439             result = reader.readRecords(this.data);
8440         }catch(e){
8441             this.fireEvent("loadexception", this, arg, null, e);
8442             callback.call(scope, null, arg, false);
8443             return;
8444         }
8445         callback.call(scope, result, arg, true);
8446     },
8447     
8448     // private
8449     update : function(params, records){
8450         
8451     }
8452 });/*
8453  * Based on:
8454  * Ext JS Library 1.1.1
8455  * Copyright(c) 2006-2007, Ext JS, LLC.
8456  *
8457  * Originally Released Under LGPL - original licence link has changed is not relivant.
8458  *
8459  * Fork - LGPL
8460  * <script type="text/javascript">
8461  */
8462 /**
8463  * @class Roo.data.HttpProxy
8464  * @extends Roo.data.DataProxy
8465  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
8466  * configured to reference a certain URL.<br><br>
8467  * <p>
8468  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
8469  * from which the running page was served.<br><br>
8470  * <p>
8471  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
8472  * <p>
8473  * Be aware that to enable the browser to parse an XML document, the server must set
8474  * the Content-Type header in the HTTP response to "text/xml".
8475  * @constructor
8476  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
8477  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
8478  * will be used to make the request.
8479  */
8480 Roo.data.HttpProxy = function(conn){
8481     Roo.data.HttpProxy.superclass.constructor.call(this);
8482     // is conn a conn config or a real conn?
8483     this.conn = conn;
8484     this.useAjax = !conn || !conn.events;
8485   
8486 };
8487
8488 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
8489     // thse are take from connection...
8490     
8491     /**
8492      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
8493      */
8494     /**
8495      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
8496      * extra parameters to each request made by this object. (defaults to undefined)
8497      */
8498     /**
8499      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
8500      *  to each request made by this object. (defaults to undefined)
8501      */
8502     /**
8503      * @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)
8504      */
8505     /**
8506      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
8507      */
8508      /**
8509      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
8510      * @type Boolean
8511      */
8512   
8513
8514     /**
8515      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
8516      * @type Boolean
8517      */
8518     /**
8519      * Return the {@link Roo.data.Connection} object being used by this Proxy.
8520      * @return {Connection} The Connection object. This object may be used to subscribe to events on
8521      * a finer-grained basis than the DataProxy events.
8522      */
8523     getConnection : function(){
8524         return this.useAjax ? Roo.Ajax : this.conn;
8525     },
8526
8527     /**
8528      * Load data from the configured {@link Roo.data.Connection}, read the data object into
8529      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
8530      * process that block using the passed callback.
8531      * @param {Object} params An object containing properties which are to be used as HTTP parameters
8532      * for the request to the remote server.
8533      * @param {Roo.data.DataReader} reader The Reader object which converts the data
8534      * object into a block of Roo.data.Records.
8535      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
8536      * The function must be passed <ul>
8537      * <li>The Record block object</li>
8538      * <li>The "arg" argument from the load function</li>
8539      * <li>A boolean success indicator</li>
8540      * </ul>
8541      * @param {Object} scope The scope in which to call the callback
8542      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
8543      */
8544     load : function(params, reader, callback, scope, arg){
8545         if(this.fireEvent("beforeload", this, params) !== false){
8546             var  o = {
8547                 params : params || {},
8548                 request: {
8549                     callback : callback,
8550                     scope : scope,
8551                     arg : arg
8552                 },
8553                 reader: reader,
8554                 callback : this.loadResponse,
8555                 scope: this
8556             };
8557             if(this.useAjax){
8558                 Roo.applyIf(o, this.conn);
8559                 if(this.activeRequest){
8560                     Roo.Ajax.abort(this.activeRequest);
8561                 }
8562                 this.activeRequest = Roo.Ajax.request(o);
8563             }else{
8564                 this.conn.request(o);
8565             }
8566         }else{
8567             callback.call(scope||this, null, arg, false);
8568         }
8569     },
8570
8571     // private
8572     loadResponse : function(o, success, response){
8573         delete this.activeRequest;
8574         if(!success){
8575             this.fireEvent("loadexception", this, o, response);
8576             o.request.callback.call(o.request.scope, null, o.request.arg, false);
8577             return;
8578         }
8579         var result;
8580         try {
8581             result = o.reader.read(response);
8582         }catch(e){
8583             this.fireEvent("loadexception", this, o, response, e);
8584             o.request.callback.call(o.request.scope, null, o.request.arg, false);
8585             return;
8586         }
8587         
8588         this.fireEvent("load", this, o, o.request.arg);
8589         o.request.callback.call(o.request.scope, result, o.request.arg, true);
8590     },
8591
8592     // private
8593     update : function(dataSet){
8594
8595     },
8596
8597     // private
8598     updateResponse : function(dataSet){
8599
8600     }
8601 });/*
8602  * Based on:
8603  * Ext JS Library 1.1.1
8604  * Copyright(c) 2006-2007, Ext JS, LLC.
8605  *
8606  * Originally Released Under LGPL - original licence link has changed is not relivant.
8607  *
8608  * Fork - LGPL
8609  * <script type="text/javascript">
8610  */
8611
8612 /**
8613  * @class Roo.data.ScriptTagProxy
8614  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
8615  * other than the originating domain of the running page.<br><br>
8616  * <p>
8617  * <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
8618  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
8619  * <p>
8620  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
8621  * source code that is used as the source inside a &lt;script> tag.<br><br>
8622  * <p>
8623  * In order for the browser to process the returned data, the server must wrap the data object
8624  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
8625  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
8626  * depending on whether the callback name was passed:
8627  * <p>
8628  * <pre><code>
8629 boolean scriptTag = false;
8630 String cb = request.getParameter("callback");
8631 if (cb != null) {
8632     scriptTag = true;
8633     response.setContentType("text/javascript");
8634 } else {
8635     response.setContentType("application/x-json");
8636 }
8637 Writer out = response.getWriter();
8638 if (scriptTag) {
8639     out.write(cb + "(");
8640 }
8641 out.print(dataBlock.toJsonString());
8642 if (scriptTag) {
8643     out.write(");");
8644 }
8645 </pre></code>
8646  *
8647  * @constructor
8648  * @param {Object} config A configuration object.
8649  */
8650 Roo.data.ScriptTagProxy = function(config){
8651     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
8652     Roo.apply(this, config);
8653     this.head = document.getElementsByTagName("head")[0];
8654 };
8655
8656 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
8657
8658 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
8659     /**
8660      * @cfg {String} url The URL from which to request the data object.
8661      */
8662     /**
8663      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
8664      */
8665     timeout : 30000,
8666     /**
8667      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
8668      * the server the name of the callback function set up by the load call to process the returned data object.
8669      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
8670      * javascript output which calls this named function passing the data object as its only parameter.
8671      */
8672     callbackParam : "callback",
8673     /**
8674      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
8675      * name to the request.
8676      */
8677     nocache : true,
8678
8679     /**
8680      * Load data from the configured URL, read the data object into
8681      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
8682      * process that block using the passed callback.
8683      * @param {Object} params An object containing properties which are to be used as HTTP parameters
8684      * for the request to the remote server.
8685      * @param {Roo.data.DataReader} reader The Reader object which converts the data
8686      * object into a block of Roo.data.Records.
8687      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
8688      * The function must be passed <ul>
8689      * <li>The Record block object</li>
8690      * <li>The "arg" argument from the load function</li>
8691      * <li>A boolean success indicator</li>
8692      * </ul>
8693      * @param {Object} scope The scope in which to call the callback
8694      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
8695      */
8696     load : function(params, reader, callback, scope, arg){
8697         if(this.fireEvent("beforeload", this, params) !== false){
8698
8699             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
8700
8701             var url = this.url;
8702             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
8703             if(this.nocache){
8704                 url += "&_dc=" + (new Date().getTime());
8705             }
8706             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
8707             var trans = {
8708                 id : transId,
8709                 cb : "stcCallback"+transId,
8710                 scriptId : "stcScript"+transId,
8711                 params : params,
8712                 arg : arg,
8713                 url : url,
8714                 callback : callback,
8715                 scope : scope,
8716                 reader : reader
8717             };
8718             var conn = this;
8719
8720             window[trans.cb] = function(o){
8721                 conn.handleResponse(o, trans);
8722             };
8723
8724             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
8725
8726             if(this.autoAbort !== false){
8727                 this.abort();
8728             }
8729
8730             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
8731
8732             var script = document.createElement("script");
8733             script.setAttribute("src", url);
8734             script.setAttribute("type", "text/javascript");
8735             script.setAttribute("id", trans.scriptId);
8736             this.head.appendChild(script);
8737
8738             this.trans = trans;
8739         }else{
8740             callback.call(scope||this, null, arg, false);
8741         }
8742     },
8743
8744     // private
8745     isLoading : function(){
8746         return this.trans ? true : false;
8747     },
8748
8749     /**
8750      * Abort the current server request.
8751      */
8752     abort : function(){
8753         if(this.isLoading()){
8754             this.destroyTrans(this.trans);
8755         }
8756     },
8757
8758     // private
8759     destroyTrans : function(trans, isLoaded){
8760         this.head.removeChild(document.getElementById(trans.scriptId));
8761         clearTimeout(trans.timeoutId);
8762         if(isLoaded){
8763             window[trans.cb] = undefined;
8764             try{
8765                 delete window[trans.cb];
8766             }catch(e){}
8767         }else{
8768             // if hasn't been loaded, wait for load to remove it to prevent script error
8769             window[trans.cb] = function(){
8770                 window[trans.cb] = undefined;
8771                 try{
8772                     delete window[trans.cb];
8773                 }catch(e){}
8774             };
8775         }
8776     },
8777
8778     // private
8779     handleResponse : function(o, trans){
8780         this.trans = false;
8781         this.destroyTrans(trans, true);
8782         var result;
8783         try {
8784             result = trans.reader.readRecords(o);
8785         }catch(e){
8786             this.fireEvent("loadexception", this, o, trans.arg, e);
8787             trans.callback.call(trans.scope||window, null, trans.arg, false);
8788             return;
8789         }
8790         this.fireEvent("load", this, o, trans.arg);
8791         trans.callback.call(trans.scope||window, result, trans.arg, true);
8792     },
8793
8794     // private
8795     handleFailure : function(trans){
8796         this.trans = false;
8797         this.destroyTrans(trans, false);
8798         this.fireEvent("loadexception", this, null, trans.arg);
8799         trans.callback.call(trans.scope||window, null, trans.arg, false);
8800     }
8801 });/*
8802  * Based on:
8803  * Ext JS Library 1.1.1
8804  * Copyright(c) 2006-2007, Ext JS, LLC.
8805  *
8806  * Originally Released Under LGPL - original licence link has changed is not relivant.
8807  *
8808  * Fork - LGPL
8809  * <script type="text/javascript">
8810  */
8811
8812 /**
8813  * @class Roo.data.JsonReader
8814  * @extends Roo.data.DataReader
8815  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
8816  * based on mappings in a provided Roo.data.Record constructor.
8817  * 
8818  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
8819  * in the reply previously. 
8820  * 
8821  * <p>
8822  * Example code:
8823  * <pre><code>
8824 var RecordDef = Roo.data.Record.create([
8825     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
8826     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
8827 ]);
8828 var myReader = new Roo.data.JsonReader({
8829     totalProperty: "results",    // The property which contains the total dataset size (optional)
8830     root: "rows",                // The property which contains an Array of row objects
8831     id: "id"                     // The property within each row object that provides an ID for the record (optional)
8832 }, RecordDef);
8833 </code></pre>
8834  * <p>
8835  * This would consume a JSON file like this:
8836  * <pre><code>
8837 { 'results': 2, 'rows': [
8838     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
8839     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
8840 }
8841 </code></pre>
8842  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
8843  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
8844  * paged from the remote server.
8845  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
8846  * @cfg {String} root name of the property which contains the Array of row objects.
8847  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
8848  * @constructor
8849  * Create a new JsonReader
8850  * @param {Object} meta Metadata configuration options
8851  * @param {Object} recordType Either an Array of field definition objects,
8852  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
8853  */
8854 Roo.data.JsonReader = function(meta, recordType){
8855     
8856     meta = meta || {};
8857     // set some defaults:
8858     Roo.applyIf(meta, {
8859         totalProperty: 'total',
8860         successProperty : 'success',
8861         root : 'data',
8862         id : 'id'
8863     });
8864     
8865     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
8866 };
8867 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
8868     
8869     /**
8870      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
8871      * Used by Store query builder to append _requestMeta to params.
8872      * 
8873      */
8874     metaFromRemote : false,
8875     /**
8876      * This method is only used by a DataProxy which has retrieved data from a remote server.
8877      * @param {Object} response The XHR object which contains the JSON data in its responseText.
8878      * @return {Object} data A data block which is used by an Roo.data.Store object as
8879      * a cache of Roo.data.Records.
8880      */
8881     read : function(response){
8882         var json = response.responseText;
8883        
8884         var o = /* eval:var:o */ eval("("+json+")");
8885         if(!o) {
8886             throw {message: "JsonReader.read: Json object not found"};
8887         }
8888         
8889         if(o.metaData){
8890             
8891             delete this.ef;
8892             this.metaFromRemote = true;
8893             this.meta = o.metaData;
8894             this.recordType = Roo.data.Record.create(o.metaData.fields);
8895             this.onMetaChange(this.meta, this.recordType, o);
8896         }
8897         return this.readRecords(o);
8898     },
8899
8900     // private function a store will implement
8901     onMetaChange : function(meta, recordType, o){
8902
8903     },
8904
8905     /**
8906          * @ignore
8907          */
8908     simpleAccess: function(obj, subsc) {
8909         return obj[subsc];
8910     },
8911
8912         /**
8913          * @ignore
8914          */
8915     getJsonAccessor: function(){
8916         var re = /[\[\.]/;
8917         return function(expr) {
8918             try {
8919                 return(re.test(expr))
8920                     ? new Function("obj", "return obj." + expr)
8921                     : function(obj){
8922                         return obj[expr];
8923                     };
8924             } catch(e){}
8925             return Roo.emptyFn;
8926         };
8927     }(),
8928
8929     /**
8930      * Create a data block containing Roo.data.Records from an XML document.
8931      * @param {Object} o An object which contains an Array of row objects in the property specified
8932      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
8933      * which contains the total size of the dataset.
8934      * @return {Object} data A data block which is used by an Roo.data.Store object as
8935      * a cache of Roo.data.Records.
8936      */
8937     readRecords : function(o){
8938         /**
8939          * After any data loads, the raw JSON data is available for further custom processing.
8940          * @type Object
8941          */
8942         this.o = o;
8943         var s = this.meta, Record = this.recordType,
8944             f = Record.prototype.fields, fi = f.items, fl = f.length;
8945
8946 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
8947         if (!this.ef) {
8948             if(s.totalProperty) {
8949                     this.getTotal = this.getJsonAccessor(s.totalProperty);
8950                 }
8951                 if(s.successProperty) {
8952                     this.getSuccess = this.getJsonAccessor(s.successProperty);
8953                 }
8954                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
8955                 if (s.id) {
8956                         var g = this.getJsonAccessor(s.id);
8957                         this.getId = function(rec) {
8958                                 var r = g(rec);
8959                                 return (r === undefined || r === "") ? null : r;
8960                         };
8961                 } else {
8962                         this.getId = function(){return null;};
8963                 }
8964             this.ef = [];
8965             for(var jj = 0; jj < fl; jj++){
8966                 f = fi[jj];
8967                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
8968                 this.ef[jj] = this.getJsonAccessor(map);
8969             }
8970         }
8971
8972         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
8973         if(s.totalProperty){
8974             var vt = parseInt(this.getTotal(o), 10);
8975             if(!isNaN(vt)){
8976                 totalRecords = vt;
8977             }
8978         }
8979         if(s.successProperty){
8980             var vs = this.getSuccess(o);
8981             if(vs === false || vs === 'false'){
8982                 success = false;
8983             }
8984         }
8985         var records = [];
8986             for(var i = 0; i < c; i++){
8987                     var n = root[i];
8988                 var values = {};
8989                 var id = this.getId(n);
8990                 for(var j = 0; j < fl; j++){
8991                     f = fi[j];
8992                 var v = this.ef[j](n);
8993                 if (!f.convert) {
8994                     Roo.log('missing convert for ' + f.name);
8995                     Roo.log(f);
8996                     continue;
8997                 }
8998                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
8999                 }
9000                 var record = new Record(values, id);
9001                 record.json = n;
9002                 records[i] = record;
9003             }
9004             return {
9005             raw : o,
9006                 success : success,
9007                 records : records,
9008                 totalRecords : totalRecords
9009             };
9010     }
9011 });/*
9012  * Based on:
9013  * Ext JS Library 1.1.1
9014  * Copyright(c) 2006-2007, Ext JS, LLC.
9015  *
9016  * Originally Released Under LGPL - original licence link has changed is not relivant.
9017  *
9018  * Fork - LGPL
9019  * <script type="text/javascript">
9020  */
9021
9022 /**
9023  * @class Roo.data.ArrayReader
9024  * @extends Roo.data.DataReader
9025  * Data reader class to create an Array of Roo.data.Record objects from an Array.
9026  * Each element of that Array represents a row of data fields. The
9027  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
9028  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
9029  * <p>
9030  * Example code:.
9031  * <pre><code>
9032 var RecordDef = Roo.data.Record.create([
9033     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
9034     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
9035 ]);
9036 var myReader = new Roo.data.ArrayReader({
9037     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
9038 }, RecordDef);
9039 </code></pre>
9040  * <p>
9041  * This would consume an Array like this:
9042  * <pre><code>
9043 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
9044   </code></pre>
9045  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
9046  * @constructor
9047  * Create a new JsonReader
9048  * @param {Object} meta Metadata configuration options.
9049  * @param {Object} recordType Either an Array of field definition objects
9050  * as specified to {@link Roo.data.Record#create},
9051  * or an {@link Roo.data.Record} object
9052  * created using {@link Roo.data.Record#create}.
9053  */
9054 Roo.data.ArrayReader = function(meta, recordType){
9055     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
9056 };
9057
9058 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
9059     /**
9060      * Create a data block containing Roo.data.Records from an XML document.
9061      * @param {Object} o An Array of row objects which represents the dataset.
9062      * @return {Object} data A data block which is used by an Roo.data.Store object as
9063      * a cache of Roo.data.Records.
9064      */
9065     readRecords : function(o){
9066         var sid = this.meta ? this.meta.id : null;
9067         var recordType = this.recordType, fields = recordType.prototype.fields;
9068         var records = [];
9069         var root = o;
9070             for(var i = 0; i < root.length; i++){
9071                     var n = root[i];
9072                 var values = {};
9073                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
9074                 for(var j = 0, jlen = fields.length; j < jlen; j++){
9075                 var f = fields.items[j];
9076                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
9077                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
9078                 v = f.convert(v);
9079                 values[f.name] = v;
9080             }
9081                 var record = new recordType(values, id);
9082                 record.json = n;
9083                 records[records.length] = record;
9084             }
9085             return {
9086                 records : records,
9087                 totalRecords : records.length
9088             };
9089     }
9090 });/*
9091  * - LGPL
9092  * * 
9093  */
9094
9095 /**
9096  * @class Roo.bootstrap.ComboBox
9097  * @extends Roo.bootstrap.TriggerField
9098  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
9099  * @cfg {Boolean} append (true|false) default false
9100  * @constructor
9101  * Create a new ComboBox.
9102  * @param {Object} config Configuration options
9103  */
9104 Roo.bootstrap.ComboBox = function(config){
9105     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
9106     this.addEvents({
9107         /**
9108          * @event expand
9109          * Fires when the dropdown list is expanded
9110              * @param {Roo.bootstrap.ComboBox} combo This combo box
9111              */
9112         'expand' : true,
9113         /**
9114          * @event collapse
9115          * Fires when the dropdown list is collapsed
9116              * @param {Roo.bootstrap.ComboBox} combo This combo box
9117              */
9118         'collapse' : true,
9119         /**
9120          * @event beforeselect
9121          * Fires before a list item is selected. Return false to cancel the selection.
9122              * @param {Roo.bootstrap.ComboBox} combo This combo box
9123              * @param {Roo.data.Record} record The data record returned from the underlying store
9124              * @param {Number} index The index of the selected item in the dropdown list
9125              */
9126         'beforeselect' : true,
9127         /**
9128          * @event select
9129          * Fires when a list item is selected
9130              * @param {Roo.bootstrap.ComboBox} combo This combo box
9131              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
9132              * @param {Number} index The index of the selected item in the dropdown list
9133              */
9134         'select' : true,
9135         /**
9136          * @event beforequery
9137          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
9138          * The event object passed has these properties:
9139              * @param {Roo.bootstrap.ComboBox} combo This combo box
9140              * @param {String} query The query
9141              * @param {Boolean} forceAll true to force "all" query
9142              * @param {Boolean} cancel true to cancel the query
9143              * @param {Object} e The query event object
9144              */
9145         'beforequery': true,
9146          /**
9147          * @event add
9148          * Fires when the 'add' icon is pressed (add a listener to enable add button)
9149              * @param {Roo.bootstrap.ComboBox} combo This combo box
9150              */
9151         'add' : true,
9152         /**
9153          * @event edit
9154          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
9155              * @param {Roo.bootstrap.ComboBox} combo This combo box
9156              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
9157              */
9158         'edit' : true,
9159         /**
9160          * @event remove
9161          * Fires when the remove value from the combobox array
9162              * @param {Roo.bootstrap.ComboBox} combo This combo box
9163              */
9164         'remove' : true
9165         
9166     });
9167     
9168     
9169     this.selectedIndex = -1;
9170     if(this.mode == 'local'){
9171         if(config.queryDelay === undefined){
9172             this.queryDelay = 10;
9173         }
9174         if(config.minChars === undefined){
9175             this.minChars = 0;
9176         }
9177     }
9178 };
9179
9180 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
9181      
9182     /**
9183      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
9184      * rendering into an Roo.Editor, defaults to false)
9185      */
9186     /**
9187      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
9188      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
9189      */
9190     /**
9191      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
9192      */
9193     /**
9194      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
9195      * the dropdown list (defaults to undefined, with no header element)
9196      */
9197
9198      /**
9199      * @cfg {String/Roo.Template} tpl The template to use to render the output
9200      */
9201      
9202      /**
9203      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
9204      */
9205     listWidth: undefined,
9206     /**
9207      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
9208      * mode = 'remote' or 'text' if mode = 'local')
9209      */
9210     displayField: undefined,
9211     /**
9212      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
9213      * mode = 'remote' or 'value' if mode = 'local'). 
9214      * Note: use of a valueField requires the user make a selection
9215      * in order for a value to be mapped.
9216      */
9217     valueField: undefined,
9218     
9219     
9220     /**
9221      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
9222      * field's data value (defaults to the underlying DOM element's name)
9223      */
9224     hiddenName: undefined,
9225     /**
9226      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
9227      */
9228     listClass: '',
9229     /**
9230      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
9231      */
9232     selectedClass: 'active',
9233     
9234     /**
9235      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
9236      */
9237     shadow:'sides',
9238     /**
9239      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
9240      * anchor positions (defaults to 'tl-bl')
9241      */
9242     listAlign: 'tl-bl?',
9243     /**
9244      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
9245      */
9246     maxHeight: 300,
9247     /**
9248      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
9249      * query specified by the allQuery config option (defaults to 'query')
9250      */
9251     triggerAction: 'query',
9252     /**
9253      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
9254      * (defaults to 4, does not apply if editable = false)
9255      */
9256     minChars : 4,
9257     /**
9258      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
9259      * delay (typeAheadDelay) if it matches a known value (defaults to false)
9260      */
9261     typeAhead: false,
9262     /**
9263      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
9264      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
9265      */
9266     queryDelay: 500,
9267     /**
9268      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
9269      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
9270      */
9271     pageSize: 0,
9272     /**
9273      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
9274      * when editable = true (defaults to false)
9275      */
9276     selectOnFocus:false,
9277     /**
9278      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
9279      */
9280     queryParam: 'query',
9281     /**
9282      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
9283      * when mode = 'remote' (defaults to 'Loading...')
9284      */
9285     loadingText: 'Loading...',
9286     /**
9287      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
9288      */
9289     resizable: false,
9290     /**
9291      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
9292      */
9293     handleHeight : 8,
9294     /**
9295      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
9296      * traditional select (defaults to true)
9297      */
9298     editable: true,
9299     /**
9300      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
9301      */
9302     allQuery: '',
9303     /**
9304      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
9305      */
9306     mode: 'remote',
9307     /**
9308      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
9309      * listWidth has a higher value)
9310      */
9311     minListWidth : 70,
9312     /**
9313      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
9314      * allow the user to set arbitrary text into the field (defaults to false)
9315      */
9316     forceSelection:false,
9317     /**
9318      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
9319      * if typeAhead = true (defaults to 250)
9320      */
9321     typeAheadDelay : 250,
9322     /**
9323      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
9324      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
9325      */
9326     valueNotFoundText : undefined,
9327     /**
9328      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
9329      */
9330     blockFocus : false,
9331     
9332     /**
9333      * @cfg {Boolean} disableClear Disable showing of clear button.
9334      */
9335     disableClear : false,
9336     /**
9337      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
9338      */
9339     alwaysQuery : false,
9340     
9341     /**
9342      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
9343      */
9344     multiple : false,
9345     
9346     //private
9347     addicon : false,
9348     editicon: false,
9349     
9350     page: 0,
9351     hasQuery: false,
9352     append: false,
9353     loadNext: false,
9354     item: [],
9355     
9356     // element that contains real text value.. (when hidden is used..)
9357      
9358     // private
9359     initEvents: function(){
9360         
9361         if (!this.store) {
9362             throw "can not find store for combo";
9363         }
9364         this.store = Roo.factory(this.store, Roo.data);
9365         
9366         
9367         
9368         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
9369         
9370         
9371         if(this.hiddenName){
9372             
9373             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
9374             
9375             this.hiddenField.dom.value =
9376                 this.hiddenValue !== undefined ? this.hiddenValue :
9377                 this.value !== undefined ? this.value : '';
9378
9379             // prevent input submission
9380             this.el.dom.removeAttribute('name');
9381             this.hiddenField.dom.setAttribute('name', this.hiddenName);
9382              
9383              
9384         }
9385         //if(Roo.isGecko){
9386         //    this.el.dom.setAttribute('autocomplete', 'off');
9387         //}
9388
9389         var cls = 'x-combo-list';
9390         this.list = this.el.select('ul.dropdown-menu',true).first();
9391
9392         //this.list = new Roo.Layer({
9393         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
9394         //});
9395         
9396         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
9397         this.list.setWidth(lw);
9398         
9399         this.list.on('mouseover', this.onViewOver, this);
9400         this.list.on('mousemove', this.onViewMove, this);
9401         
9402         this.list.on('scroll', this.onViewScroll, this);
9403         
9404         /*
9405         this.list.swallowEvent('mousewheel');
9406         this.assetHeight = 0;
9407
9408         if(this.title){
9409             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
9410             this.assetHeight += this.header.getHeight();
9411         }
9412
9413         this.innerList = this.list.createChild({cls:cls+'-inner'});
9414         this.innerList.on('mouseover', this.onViewOver, this);
9415         this.innerList.on('mousemove', this.onViewMove, this);
9416         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
9417         
9418         if(this.allowBlank && !this.pageSize && !this.disableClear){
9419             this.footer = this.list.createChild({cls:cls+'-ft'});
9420             this.pageTb = new Roo.Toolbar(this.footer);
9421            
9422         }
9423         if(this.pageSize){
9424             this.footer = this.list.createChild({cls:cls+'-ft'});
9425             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
9426                     {pageSize: this.pageSize});
9427             
9428         }
9429         
9430         if (this.pageTb && this.allowBlank && !this.disableClear) {
9431             var _this = this;
9432             this.pageTb.add(new Roo.Toolbar.Fill(), {
9433                 cls: 'x-btn-icon x-btn-clear',
9434                 text: '&#160;',
9435                 handler: function()
9436                 {
9437                     _this.collapse();
9438                     _this.clearValue();
9439                     _this.onSelect(false, -1);
9440                 }
9441             });
9442         }
9443         if (this.footer) {
9444             this.assetHeight += this.footer.getHeight();
9445         }
9446         */
9447             
9448         if(!this.tpl){
9449             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
9450         }
9451
9452         this.view = new Roo.View(this.el.select('ul.dropdown-menu',true).first(), this.tpl, {
9453             singleSelect:true, store: this.store, selectedClass: this.selectedClass
9454         });
9455         //this.view.wrapEl.setDisplayed(false);
9456         this.view.on('click', this.onViewClick, this);
9457         
9458         
9459         
9460         this.store.on('beforeload', this.onBeforeLoad, this);
9461         this.store.on('load', this.onLoad, this);
9462         this.store.on('loadexception', this.onLoadException, this);
9463         /*
9464         if(this.resizable){
9465             this.resizer = new Roo.Resizable(this.list,  {
9466                pinned:true, handles:'se'
9467             });
9468             this.resizer.on('resize', function(r, w, h){
9469                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
9470                 this.listWidth = w;
9471                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
9472                 this.restrictHeight();
9473             }, this);
9474             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
9475         }
9476         */
9477         if(!this.editable){
9478             this.editable = true;
9479             this.setEditable(false);
9480         }
9481         
9482         /*
9483         
9484         if (typeof(this.events.add.listeners) != 'undefined') {
9485             
9486             this.addicon = this.wrap.createChild(
9487                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
9488        
9489             this.addicon.on('click', function(e) {
9490                 this.fireEvent('add', this);
9491             }, this);
9492         }
9493         if (typeof(this.events.edit.listeners) != 'undefined') {
9494             
9495             this.editicon = this.wrap.createChild(
9496                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
9497             if (this.addicon) {
9498                 this.editicon.setStyle('margin-left', '40px');
9499             }
9500             this.editicon.on('click', function(e) {
9501                 
9502                 // we fire even  if inothing is selected..
9503                 this.fireEvent('edit', this, this.lastData );
9504                 
9505             }, this);
9506         }
9507         */
9508         
9509         this.keyNav = new Roo.KeyNav(this.inputEl(), {
9510             "up" : function(e){
9511                 this.inKeyMode = true;
9512                 this.selectPrev();
9513             },
9514
9515             "down" : function(e){
9516                 if(!this.isExpanded()){
9517                     this.onTriggerClick();
9518                 }else{
9519                     this.inKeyMode = true;
9520                     this.selectNext();
9521                 }
9522             },
9523
9524             "enter" : function(e){
9525                 this.onViewClick();
9526                 //return true;
9527             },
9528
9529             "esc" : function(e){
9530                 this.collapse();
9531             },
9532
9533             "tab" : function(e){
9534                 this.collapse();
9535                 
9536                 if(this.fireEvent("specialkey", this, e)){
9537                     this.onViewClick(false);
9538                 }
9539                 
9540                 return true;
9541             },
9542
9543             scope : this,
9544
9545             doRelay : function(foo, bar, hname){
9546                 if(hname == 'down' || this.scope.isExpanded()){
9547                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
9548                 }
9549                 return true;
9550             },
9551
9552             forceKeyDown: true
9553         });
9554         
9555         
9556         this.queryDelay = Math.max(this.queryDelay || 10,
9557                 this.mode == 'local' ? 10 : 250);
9558         
9559         
9560         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
9561         
9562         if(this.typeAhead){
9563             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
9564         }
9565         if(this.editable !== false){
9566             this.inputEl().on("keyup", this.onKeyUp, this);
9567         }
9568         if(this.forceSelection){
9569             this.inputEl().on('blur', this.doForce, this);
9570         }
9571         
9572         if(this.multiple){
9573             this.choices = this.el.select('ul.select2-choices', true).first();
9574             this.searchField = this.el.select('ul li.select2-search-field', true).first();
9575         }
9576     },
9577
9578     onDestroy : function(){
9579         if(this.view){
9580             this.view.setStore(null);
9581             this.view.el.removeAllListeners();
9582             this.view.el.remove();
9583             this.view.purgeListeners();
9584         }
9585         if(this.list){
9586             this.list.dom.innerHTML  = '';
9587         }
9588         if(this.store){
9589             this.store.un('beforeload', this.onBeforeLoad, this);
9590             this.store.un('load', this.onLoad, this);
9591             this.store.un('loadexception', this.onLoadException, this);
9592         }
9593         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
9594     },
9595
9596     // private
9597     fireKey : function(e){
9598         if(e.isNavKeyPress() && !this.list.isVisible()){
9599             this.fireEvent("specialkey", this, e);
9600         }
9601     },
9602
9603     // private
9604     onResize: function(w, h){
9605 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
9606 //        
9607 //        if(typeof w != 'number'){
9608 //            // we do not handle it!?!?
9609 //            return;
9610 //        }
9611 //        var tw = this.trigger.getWidth();
9612 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
9613 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
9614 //        var x = w - tw;
9615 //        this.inputEl().setWidth( this.adjustWidth('input', x));
9616 //            
9617 //        //this.trigger.setStyle('left', x+'px');
9618 //        
9619 //        if(this.list && this.listWidth === undefined){
9620 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
9621 //            this.list.setWidth(lw);
9622 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
9623 //        }
9624         
9625     
9626         
9627     },
9628
9629     /**
9630      * Allow or prevent the user from directly editing the field text.  If false is passed,
9631      * the user will only be able to select from the items defined in the dropdown list.  This method
9632      * is the runtime equivalent of setting the 'editable' config option at config time.
9633      * @param {Boolean} value True to allow the user to directly edit the field text
9634      */
9635     setEditable : function(value){
9636         if(value == this.editable){
9637             return;
9638         }
9639         this.editable = value;
9640         if(!value){
9641             this.inputEl().dom.setAttribute('readOnly', true);
9642             this.inputEl().on('mousedown', this.onTriggerClick,  this);
9643             this.inputEl().addClass('x-combo-noedit');
9644         }else{
9645             this.inputEl().dom.setAttribute('readOnly', false);
9646             this.inputEl().un('mousedown', this.onTriggerClick,  this);
9647             this.inputEl().removeClass('x-combo-noedit');
9648         }
9649     },
9650
9651     // private
9652     
9653     onBeforeLoad : function(combo,opts){
9654         if(!this.hasFocus){
9655             return;
9656         }
9657          if (!opts.add) {
9658             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
9659          }
9660         this.restrictHeight();
9661         this.selectedIndex = -1;
9662     },
9663
9664     // private
9665     onLoad : function(){
9666         
9667         this.hasQuery = false;
9668         
9669         if(!this.hasFocus){
9670             return;
9671         }
9672         
9673         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
9674             this.loading.hide();
9675         }
9676         
9677         if(this.store.getCount() > 0){
9678             this.expand();
9679             this.restrictHeight();
9680             if(this.lastQuery == this.allQuery){
9681                 if(this.editable){
9682                     this.inputEl().dom.select();
9683                 }
9684                 if(!this.selectByValue(this.value, true)){
9685                     this.select(0, true);
9686                 }
9687             }else{
9688                 this.selectNext();
9689                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
9690                     this.taTask.delay(this.typeAheadDelay);
9691                 }
9692             }
9693         }else{
9694             this.onEmptyResults();
9695         }
9696         
9697         //this.el.focus();
9698     },
9699     // private
9700     onLoadException : function()
9701     {
9702         this.hasQuery = false;
9703         
9704         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
9705             this.loading.hide();
9706         }
9707         
9708         this.collapse();
9709         Roo.log(this.store.reader.jsonData);
9710         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
9711             // fixme
9712             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
9713         }
9714         
9715         
9716     },
9717     // private
9718     onTypeAhead : function(){
9719         if(this.store.getCount() > 0){
9720             var r = this.store.getAt(0);
9721             var newValue = r.data[this.displayField];
9722             var len = newValue.length;
9723             var selStart = this.getRawValue().length;
9724             
9725             if(selStart != len){
9726                 this.setRawValue(newValue);
9727                 this.selectText(selStart, newValue.length);
9728             }
9729         }
9730     },
9731
9732     // private
9733     onSelect : function(record, index){
9734         
9735         if(this.fireEvent('beforeselect', this, record, index) !== false){
9736         
9737             this.setFromData(index > -1 ? record.data : false);
9738             
9739             this.collapse();
9740             this.fireEvent('select', this, record, index);
9741         }
9742     },
9743
9744     /**
9745      * Returns the currently selected field value or empty string if no value is set.
9746      * @return {String} value The selected value
9747      */
9748     getValue : function(){
9749         
9750         if(this.multiple){
9751             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
9752         }
9753         
9754         if(this.valueField){
9755             return typeof this.value != 'undefined' ? this.value : '';
9756         }else{
9757             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
9758         }
9759     },
9760
9761     /**
9762      * Clears any text/value currently set in the field
9763      */
9764     clearValue : function(){
9765         if(this.hiddenField){
9766             this.hiddenField.dom.value = '';
9767         }
9768         this.value = '';
9769         this.setRawValue('');
9770         this.lastSelectionText = '';
9771         
9772     },
9773
9774     /**
9775      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
9776      * will be displayed in the field.  If the value does not match the data value of an existing item,
9777      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
9778      * Otherwise the field will be blank (although the value will still be set).
9779      * @param {String} value The value to match
9780      */
9781     setValue : function(v){
9782         if(this.multiple){
9783             this.syncValue();
9784             return;
9785         }
9786         
9787         var text = v;
9788         if(this.valueField){
9789             var r = this.findRecord(this.valueField, v);
9790             if(r){
9791                 text = r.data[this.displayField];
9792             }else if(this.valueNotFoundText !== undefined){
9793                 text = this.valueNotFoundText;
9794             }
9795         }
9796         this.lastSelectionText = text;
9797         if(this.hiddenField){
9798             this.hiddenField.dom.value = v;
9799         }
9800         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
9801         this.value = v;
9802     },
9803     /**
9804      * @property {Object} the last set data for the element
9805      */
9806     
9807     lastData : false,
9808     /**
9809      * Sets the value of the field based on a object which is related to the record format for the store.
9810      * @param {Object} value the value to set as. or false on reset?
9811      */
9812     setFromData : function(o){
9813         
9814         if(this.multiple){
9815             this.addItem(o);
9816             return;
9817         }
9818             
9819         var dv = ''; // display value
9820         var vv = ''; // value value..
9821         this.lastData = o;
9822         if (this.displayField) {
9823             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
9824         } else {
9825             // this is an error condition!!!
9826             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
9827         }
9828         
9829         if(this.valueField){
9830             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
9831         }
9832         
9833         if(this.hiddenField){
9834             this.hiddenField.dom.value = vv;
9835             
9836             this.lastSelectionText = dv;
9837             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
9838             this.value = vv;
9839             return;
9840         }
9841         // no hidden field.. - we store the value in 'value', but still display
9842         // display field!!!!
9843         this.lastSelectionText = dv;
9844         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
9845         this.value = vv;
9846         
9847         
9848     },
9849     // private
9850     reset : function(){
9851         // overridden so that last data is reset..
9852         this.setValue(this.originalValue);
9853         this.clearInvalid();
9854         this.lastData = false;
9855         if (this.view) {
9856             this.view.clearSelections();
9857         }
9858     },
9859     // private
9860     findRecord : function(prop, value){
9861         var record;
9862         if(this.store.getCount() > 0){
9863             this.store.each(function(r){
9864                 if(r.data[prop] == value){
9865                     record = r;
9866                     return false;
9867                 }
9868                 return true;
9869             });
9870         }
9871         return record;
9872     },
9873     
9874     getName: function()
9875     {
9876         // returns hidden if it's set..
9877         if (!this.rendered) {return ''};
9878         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
9879         
9880     },
9881     // private
9882     onViewMove : function(e, t){
9883         this.inKeyMode = false;
9884     },
9885
9886     // private
9887     onViewOver : function(e, t){
9888         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
9889             return;
9890         }
9891         var item = this.view.findItemFromChild(t);
9892         if(item){
9893             var index = this.view.indexOf(item);
9894             this.select(index, false);
9895         }
9896     },
9897
9898     // private
9899     onViewClick : function(doFocus)
9900     {
9901         var index = this.view.getSelectedIndexes()[0];
9902         var r = this.store.getAt(index);
9903         if(r){
9904             this.onSelect(r, index);
9905         }
9906         if(doFocus !== false && !this.blockFocus){
9907             this.inputEl().focus();
9908         }
9909     },
9910
9911     // private
9912     restrictHeight : function(){
9913         //this.innerList.dom.style.height = '';
9914         //var inner = this.innerList.dom;
9915         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
9916         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
9917         //this.list.beginUpdate();
9918         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
9919         this.list.alignTo(this.inputEl(), this.listAlign);
9920         //this.list.endUpdate();
9921     },
9922
9923     // private
9924     onEmptyResults : function(){
9925         this.collapse();
9926     },
9927
9928     /**
9929      * Returns true if the dropdown list is expanded, else false.
9930      */
9931     isExpanded : function(){
9932         return this.list.isVisible();
9933     },
9934
9935     /**
9936      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
9937      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
9938      * @param {String} value The data value of the item to select
9939      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
9940      * selected item if it is not currently in view (defaults to true)
9941      * @return {Boolean} True if the value matched an item in the list, else false
9942      */
9943     selectByValue : function(v, scrollIntoView){
9944         if(v !== undefined && v !== null){
9945             var r = this.findRecord(this.valueField || this.displayField, v);
9946             if(r){
9947                 this.select(this.store.indexOf(r), scrollIntoView);
9948                 return true;
9949             }
9950         }
9951         return false;
9952     },
9953
9954     /**
9955      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
9956      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
9957      * @param {Number} index The zero-based index of the list item to select
9958      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
9959      * selected item if it is not currently in view (defaults to true)
9960      */
9961     select : function(index, scrollIntoView){
9962         this.selectedIndex = index;
9963         this.view.select(index);
9964         if(scrollIntoView !== false){
9965             var el = this.view.getNode(index);
9966             if(el){
9967                 //this.innerList.scrollChildIntoView(el, false);
9968                 
9969             }
9970         }
9971     },
9972
9973     // private
9974     selectNext : function(){
9975         var ct = this.store.getCount();
9976         if(ct > 0){
9977             if(this.selectedIndex == -1){
9978                 this.select(0);
9979             }else if(this.selectedIndex < ct-1){
9980                 this.select(this.selectedIndex+1);
9981             }
9982         }
9983     },
9984
9985     // private
9986     selectPrev : function(){
9987         var ct = this.store.getCount();
9988         if(ct > 0){
9989             if(this.selectedIndex == -1){
9990                 this.select(0);
9991             }else if(this.selectedIndex != 0){
9992                 this.select(this.selectedIndex-1);
9993             }
9994         }
9995     },
9996
9997     // private
9998     onKeyUp : function(e){
9999         if(this.editable !== false && !e.isSpecialKey()){
10000             this.lastKey = e.getKey();
10001             this.dqTask.delay(this.queryDelay);
10002         }
10003     },
10004
10005     // private
10006     validateBlur : function(){
10007         return !this.list || !this.list.isVisible();   
10008     },
10009
10010     // private
10011     initQuery : function(){
10012         this.doQuery(this.getRawValue());
10013     },
10014
10015     // private
10016     doForce : function(){
10017         if(this.inputEl().dom.value.length > 0){
10018             this.inputEl().dom.value =
10019                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
10020              
10021         }
10022     },
10023
10024     /**
10025      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
10026      * query allowing the query action to be canceled if needed.
10027      * @param {String} query The SQL query to execute
10028      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
10029      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
10030      * saved in the current store (defaults to false)
10031      */
10032     doQuery : function(q, forceAll){
10033         
10034         if(q === undefined || q === null){
10035             q = '';
10036         }
10037         var qe = {
10038             query: q,
10039             forceAll: forceAll,
10040             combo: this,
10041             cancel:false
10042         };
10043         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
10044             return false;
10045         }
10046         q = qe.query;
10047         
10048         forceAll = qe.forceAll;
10049         if(forceAll === true || (q.length >= this.minChars)){
10050             
10051             this.hasQuery = true;
10052             
10053             if(this.lastQuery != q || this.alwaysQuery){
10054                 this.lastQuery = q;
10055                 if(this.mode == 'local'){
10056                     this.selectedIndex = -1;
10057                     if(forceAll){
10058                         this.store.clearFilter();
10059                     }else{
10060                         this.store.filter(this.displayField, q);
10061                     }
10062                     this.onLoad();
10063                 }else{
10064                     this.store.baseParams[this.queryParam] = q;
10065                     
10066                     var options = {params : this.getParams(q)};
10067                     
10068                     if(this.loadNext){
10069                         options.add = true;
10070                         options.params.start = this.page * this.pageSize;
10071                     }
10072                     
10073                     this.store.load(options);
10074                     this.expand();
10075                 }
10076             }else{
10077                 this.selectedIndex = -1;
10078                 this.onLoad();   
10079             }
10080         }
10081         
10082         this.loadNext = false;
10083     },
10084
10085     // private
10086     getParams : function(q){
10087         var p = {};
10088         //p[this.queryParam] = q;
10089         
10090         if(this.pageSize){
10091             p.start = 0;
10092             p.limit = this.pageSize;
10093         }
10094         return p;
10095     },
10096
10097     /**
10098      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
10099      */
10100     collapse : function(){
10101         if(!this.isExpanded()){
10102             return;
10103         }
10104         
10105         this.list.hide();
10106         Roo.get(document).un('mousedown', this.collapseIf, this);
10107         Roo.get(document).un('mousewheel', this.collapseIf, this);
10108         if (!this.editable) {
10109             Roo.get(document).un('keydown', this.listKeyPress, this);
10110         }
10111         this.fireEvent('collapse', this);
10112     },
10113
10114     // private
10115     collapseIf : function(e){
10116         var in_combo  = e.within(this.el);
10117         var in_list =  e.within(this.list);
10118         
10119         if (in_combo || in_list) {
10120             //e.stopPropagation();
10121             return;
10122         }
10123
10124         this.collapse();
10125         
10126     },
10127
10128     /**
10129      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
10130      */
10131     expand : function(){
10132        
10133         if(this.isExpanded() || !this.hasFocus){
10134             return;
10135         }
10136          Roo.log('expand');
10137         this.list.alignTo(this.inputEl(), this.listAlign);
10138         this.list.show();
10139         Roo.get(document).on('mousedown', this.collapseIf, this);
10140         Roo.get(document).on('mousewheel', this.collapseIf, this);
10141         if (!this.editable) {
10142             Roo.get(document).on('keydown', this.listKeyPress, this);
10143         }
10144         
10145         this.fireEvent('expand', this);
10146     },
10147
10148     // private
10149     // Implements the default empty TriggerField.onTriggerClick function
10150     onTriggerClick : function()
10151     {
10152         Roo.log('trigger click');
10153         
10154         if(this.disabled){
10155             return;
10156         }
10157         
10158         this.page = 0;
10159         this.loadNext = false;
10160         
10161         if(this.isExpanded()){
10162             this.collapse();
10163             if (!this.blockFocus) {
10164                 this.inputEl().focus();
10165             }
10166             
10167         }else {
10168             this.hasFocus = true;
10169             if(this.triggerAction == 'all') {
10170                 this.doQuery(this.allQuery, true);
10171             } else {
10172                 this.doQuery(this.getRawValue());
10173             }
10174             if (!this.blockFocus) {
10175                 this.inputEl().focus();
10176             }
10177         }
10178     },
10179     listKeyPress : function(e)
10180     {
10181         //Roo.log('listkeypress');
10182         // scroll to first matching element based on key pres..
10183         if (e.isSpecialKey()) {
10184             return false;
10185         }
10186         var k = String.fromCharCode(e.getKey()).toUpperCase();
10187         //Roo.log(k);
10188         var match  = false;
10189         var csel = this.view.getSelectedNodes();
10190         var cselitem = false;
10191         if (csel.length) {
10192             var ix = this.view.indexOf(csel[0]);
10193             cselitem  = this.store.getAt(ix);
10194             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
10195                 cselitem = false;
10196             }
10197             
10198         }
10199         
10200         this.store.each(function(v) { 
10201             if (cselitem) {
10202                 // start at existing selection.
10203                 if (cselitem.id == v.id) {
10204                     cselitem = false;
10205                 }
10206                 return true;
10207             }
10208                 
10209             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
10210                 match = this.store.indexOf(v);
10211                 return false;
10212             }
10213             return true;
10214         }, this);
10215         
10216         if (match === false) {
10217             return true; // no more action?
10218         }
10219         // scroll to?
10220         this.view.select(match);
10221         var sn = Roo.get(this.view.getSelectedNodes()[0])
10222         //sn.scrollIntoView(sn.dom.parentNode, false);
10223     },
10224     
10225     onViewScroll : function(e, t){
10226         
10227         if(this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
10228             return;
10229         }
10230         
10231         this.hasQuery = true;
10232         
10233         this.loading = this.list.select('.loading', true).first();
10234         
10235         if(this.loading === null){
10236             this.list.createChild({
10237                 tag: 'div',
10238                 cls: 'loading select2-more-results select2-active',
10239                 html: 'Loading more results...'
10240             })
10241             
10242             this.loading = this.list.select('.loading', true).first();
10243             
10244             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
10245             
10246             this.loading.hide();
10247         }
10248         
10249         this.loading.show();
10250         
10251         var _combo = this;
10252         
10253         this.page++;
10254         this.loadNext = true;
10255         
10256         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
10257         
10258         return;
10259     },
10260     
10261     addItem : function(o)
10262     {   
10263         var dv = ''; // display value
10264         
10265         if (this.displayField) {
10266             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
10267         } else {
10268             // this is an error condition!!!
10269             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
10270         }
10271         
10272         if(!dv.length){
10273             return;
10274         }
10275         
10276         var choice = this.choices.createChild({
10277             tag: 'li',
10278             cls: 'select2-search-choice',
10279             cn: [
10280                 {
10281                     tag: 'div',
10282                     html: dv
10283                 },
10284                 {
10285                     tag: 'a',
10286                     href: '#',
10287                     cls: 'select2-search-choice-close',
10288                     tabindex: '-1'
10289                 }
10290             ]
10291             
10292         }, this.searchField);
10293         
10294         var close = choice.select('a.select2-search-choice-close', true).first()
10295         
10296         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
10297         
10298         this.item.push(o);
10299         this.lastData = o;
10300         
10301         this.syncValue();
10302         
10303         this.inputEl().dom.value = '';
10304         
10305     },
10306     
10307     onRemoveItem : function(e, _self, o)
10308     {
10309         e.preventDefault();
10310         var index = this.item.indexOf(o.data) * 1;
10311         
10312         if( index < 0){
10313             Roo.log('not this item?!');
10314             return;
10315         }
10316         
10317         this.item.splice(index, 1);
10318         o.item.remove();
10319         
10320         this.syncValue();
10321         
10322         this.fireEvent('remove', this, e);
10323         
10324     },
10325     
10326     syncValue : function()
10327     {
10328         if(!this.item.length){
10329             this.clearValue();
10330             return;
10331         }
10332             
10333         var value = [];
10334         var _this = this;
10335         Roo.each(this.item, function(i){
10336             if(_this.valueField){
10337                 value.push(i[_this.valueField]);
10338                 return;
10339             }
10340
10341             value.push(i);
10342         });
10343
10344         this.value = value.join(',');
10345
10346         if(this.hiddenField){
10347             this.hiddenField.dom.value = this.value;
10348         }
10349     },
10350     
10351     clearItem : function()
10352     {
10353         if(!this.multiple){
10354             return;
10355         }
10356         
10357         this.item = [];
10358         
10359         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
10360            c.remove();
10361         });
10362         
10363         this.syncValue();
10364     }
10365     
10366     
10367
10368     /** 
10369     * @cfg {Boolean} grow 
10370     * @hide 
10371     */
10372     /** 
10373     * @cfg {Number} growMin 
10374     * @hide 
10375     */
10376     /** 
10377     * @cfg {Number} growMax 
10378     * @hide 
10379     */
10380     /**
10381      * @hide
10382      * @method autoSize
10383      */
10384 });
10385 /*
10386  * Based on:
10387  * Ext JS Library 1.1.1
10388  * Copyright(c) 2006-2007, Ext JS, LLC.
10389  *
10390  * Originally Released Under LGPL - original licence link has changed is not relivant.
10391  *
10392  * Fork - LGPL
10393  * <script type="text/javascript">
10394  */
10395
10396 /**
10397  * @class Roo.View
10398  * @extends Roo.util.Observable
10399  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
10400  * This class also supports single and multi selection modes. <br>
10401  * Create a data model bound view:
10402  <pre><code>
10403  var store = new Roo.data.Store(...);
10404
10405  var view = new Roo.View({
10406     el : "my-element",
10407     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
10408  
10409     singleSelect: true,
10410     selectedClass: "ydataview-selected",
10411     store: store
10412  });
10413
10414  // listen for node click?
10415  view.on("click", function(vw, index, node, e){
10416  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
10417  });
10418
10419  // load XML data
10420  dataModel.load("foobar.xml");
10421  </code></pre>
10422  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
10423  * <br><br>
10424  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
10425  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
10426  * 
10427  * Note: old style constructor is still suported (container, template, config)
10428  * 
10429  * @constructor
10430  * Create a new View
10431  * @param {Object} config The config object
10432  * 
10433  */
10434 Roo.View = function(config, depreciated_tpl, depreciated_config){
10435     
10436     if (typeof(depreciated_tpl) == 'undefined') {
10437         // new way.. - universal constructor.
10438         Roo.apply(this, config);
10439         this.el  = Roo.get(this.el);
10440     } else {
10441         // old format..
10442         this.el  = Roo.get(config);
10443         this.tpl = depreciated_tpl;
10444         Roo.apply(this, depreciated_config);
10445     }
10446     this.wrapEl  = this.el.wrap().wrap();
10447     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
10448     
10449     
10450     if(typeof(this.tpl) == "string"){
10451         this.tpl = new Roo.Template(this.tpl);
10452     } else {
10453         // support xtype ctors..
10454         this.tpl = new Roo.factory(this.tpl, Roo);
10455     }
10456     
10457     
10458     this.tpl.compile();
10459    
10460   
10461     
10462      
10463     /** @private */
10464     this.addEvents({
10465         /**
10466          * @event beforeclick
10467          * Fires before a click is processed. Returns false to cancel the default action.
10468          * @param {Roo.View} this
10469          * @param {Number} index The index of the target node
10470          * @param {HTMLElement} node The target node
10471          * @param {Roo.EventObject} e The raw event object
10472          */
10473             "beforeclick" : true,
10474         /**
10475          * @event click
10476          * Fires when a template node is clicked.
10477          * @param {Roo.View} this
10478          * @param {Number} index The index of the target node
10479          * @param {HTMLElement} node The target node
10480          * @param {Roo.EventObject} e The raw event object
10481          */
10482             "click" : true,
10483         /**
10484          * @event dblclick
10485          * Fires when a template node is double clicked.
10486          * @param {Roo.View} this
10487          * @param {Number} index The index of the target node
10488          * @param {HTMLElement} node The target node
10489          * @param {Roo.EventObject} e The raw event object
10490          */
10491             "dblclick" : true,
10492         /**
10493          * @event contextmenu
10494          * Fires when a template node is right clicked.
10495          * @param {Roo.View} this
10496          * @param {Number} index The index of the target node
10497          * @param {HTMLElement} node The target node
10498          * @param {Roo.EventObject} e The raw event object
10499          */
10500             "contextmenu" : true,
10501         /**
10502          * @event selectionchange
10503          * Fires when the selected nodes change.
10504          * @param {Roo.View} this
10505          * @param {Array} selections Array of the selected nodes
10506          */
10507             "selectionchange" : true,
10508     
10509         /**
10510          * @event beforeselect
10511          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
10512          * @param {Roo.View} this
10513          * @param {HTMLElement} node The node to be selected
10514          * @param {Array} selections Array of currently selected nodes
10515          */
10516             "beforeselect" : true,
10517         /**
10518          * @event preparedata
10519          * Fires on every row to render, to allow you to change the data.
10520          * @param {Roo.View} this
10521          * @param {Object} data to be rendered (change this)
10522          */
10523           "preparedata" : true
10524           
10525           
10526         });
10527
10528
10529
10530     this.el.on({
10531         "click": this.onClick,
10532         "dblclick": this.onDblClick,
10533         "contextmenu": this.onContextMenu,
10534         scope:this
10535     });
10536
10537     this.selections = [];
10538     this.nodes = [];
10539     this.cmp = new Roo.CompositeElementLite([]);
10540     if(this.store){
10541         this.store = Roo.factory(this.store, Roo.data);
10542         this.setStore(this.store, true);
10543     }
10544     
10545     if ( this.footer && this.footer.xtype) {
10546            
10547          var fctr = this.wrapEl.appendChild(document.createElement("div"));
10548         
10549         this.footer.dataSource = this.store
10550         this.footer.container = fctr;
10551         this.footer = Roo.factory(this.footer, Roo);
10552         fctr.insertFirst(this.el);
10553         
10554         // this is a bit insane - as the paging toolbar seems to detach the el..
10555 //        dom.parentNode.parentNode.parentNode
10556          // they get detached?
10557     }
10558     
10559     
10560     Roo.View.superclass.constructor.call(this);
10561     
10562     
10563 };
10564
10565 Roo.extend(Roo.View, Roo.util.Observable, {
10566     
10567      /**
10568      * @cfg {Roo.data.Store} store Data store to load data from.
10569      */
10570     store : false,
10571     
10572     /**
10573      * @cfg {String|Roo.Element} el The container element.
10574      */
10575     el : '',
10576     
10577     /**
10578      * @cfg {String|Roo.Template} tpl The template used by this View 
10579      */
10580     tpl : false,
10581     /**
10582      * @cfg {String} dataName the named area of the template to use as the data area
10583      *                          Works with domtemplates roo-name="name"
10584      */
10585     dataName: false,
10586     /**
10587      * @cfg {String} selectedClass The css class to add to selected nodes
10588      */
10589     selectedClass : "x-view-selected",
10590      /**
10591      * @cfg {String} emptyText The empty text to show when nothing is loaded.
10592      */
10593     emptyText : "",
10594     
10595     /**
10596      * @cfg {String} text to display on mask (default Loading)
10597      */
10598     mask : false,
10599     /**
10600      * @cfg {Boolean} multiSelect Allow multiple selection
10601      */
10602     multiSelect : false,
10603     /**
10604      * @cfg {Boolean} singleSelect Allow single selection
10605      */
10606     singleSelect:  false,
10607     
10608     /**
10609      * @cfg {Boolean} toggleSelect - selecting 
10610      */
10611     toggleSelect : false,
10612     
10613     /**
10614      * Returns the element this view is bound to.
10615      * @return {Roo.Element}
10616      */
10617     getEl : function(){
10618         return this.wrapEl;
10619     },
10620     
10621     
10622
10623     /**
10624      * Refreshes the view. - called by datachanged on the store. - do not call directly.
10625      */
10626     refresh : function(){
10627         Roo.log('refresh');
10628         var t = this.tpl;
10629         
10630         // if we are using something like 'domtemplate', then
10631         // the what gets used is:
10632         // t.applySubtemplate(NAME, data, wrapping data..)
10633         // the outer template then get' applied with
10634         //     the store 'extra data'
10635         // and the body get's added to the
10636         //      roo-name="data" node?
10637         //      <span class='roo-tpl-{name}'></span> ?????
10638         
10639         
10640         
10641         this.clearSelections();
10642         this.el.update("");
10643         var html = [];
10644         var records = this.store.getRange();
10645         if(records.length < 1) {
10646             
10647             // is this valid??  = should it render a template??
10648             
10649             this.el.update(this.emptyText);
10650             return;
10651         }
10652         var el = this.el;
10653         if (this.dataName) {
10654             this.el.update(t.apply(this.store.meta)); //????
10655             el = this.el.child('.roo-tpl-' + this.dataName);
10656         }
10657         
10658         for(var i = 0, len = records.length; i < len; i++){
10659             var data = this.prepareData(records[i].data, i, records[i]);
10660             this.fireEvent("preparedata", this, data, i, records[i]);
10661             html[html.length] = Roo.util.Format.trim(
10662                 this.dataName ?
10663                     t.applySubtemplate(this.dataName, data, this.store.meta) :
10664                     t.apply(data)
10665             );
10666         }
10667         
10668         
10669         
10670         el.update(html.join(""));
10671         this.nodes = el.dom.childNodes;
10672         this.updateIndexes(0);
10673     },
10674     
10675
10676     /**
10677      * Function to override to reformat the data that is sent to
10678      * the template for each node.
10679      * DEPRICATED - use the preparedata event handler.
10680      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
10681      * a JSON object for an UpdateManager bound view).
10682      */
10683     prepareData : function(data, index, record)
10684     {
10685         this.fireEvent("preparedata", this, data, index, record);
10686         return data;
10687     },
10688
10689     onUpdate : function(ds, record){
10690          Roo.log('on update');   
10691         this.clearSelections();
10692         var index = this.store.indexOf(record);
10693         var n = this.nodes[index];
10694         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
10695         n.parentNode.removeChild(n);
10696         this.updateIndexes(index, index);
10697     },
10698
10699     
10700     
10701 // --------- FIXME     
10702     onAdd : function(ds, records, index)
10703     {
10704         Roo.log(['on Add', ds, records, index] );        
10705         this.clearSelections();
10706         if(this.nodes.length == 0){
10707             this.refresh();
10708             return;
10709         }
10710         var n = this.nodes[index];
10711         for(var i = 0, len = records.length; i < len; i++){
10712             var d = this.prepareData(records[i].data, i, records[i]);
10713             if(n){
10714                 this.tpl.insertBefore(n, d);
10715             }else{
10716                 
10717                 this.tpl.append(this.el, d);
10718             }
10719         }
10720         this.updateIndexes(index);
10721     },
10722
10723     onRemove : function(ds, record, index){
10724         Roo.log('onRemove');
10725         this.clearSelections();
10726         var el = this.dataName  ?
10727             this.el.child('.roo-tpl-' + this.dataName) :
10728             this.el; 
10729         
10730         el.dom.removeChild(this.nodes[index]);
10731         this.updateIndexes(index);
10732     },
10733
10734     /**
10735      * Refresh an individual node.
10736      * @param {Number} index
10737      */
10738     refreshNode : function(index){
10739         this.onUpdate(this.store, this.store.getAt(index));
10740     },
10741
10742     updateIndexes : function(startIndex, endIndex){
10743         var ns = this.nodes;
10744         startIndex = startIndex || 0;
10745         endIndex = endIndex || ns.length - 1;
10746         for(var i = startIndex; i <= endIndex; i++){
10747             ns[i].nodeIndex = i;
10748         }
10749     },
10750
10751     /**
10752      * Changes the data store this view uses and refresh the view.
10753      * @param {Store} store
10754      */
10755     setStore : function(store, initial){
10756         if(!initial && this.store){
10757             this.store.un("datachanged", this.refresh);
10758             this.store.un("add", this.onAdd);
10759             this.store.un("remove", this.onRemove);
10760             this.store.un("update", this.onUpdate);
10761             this.store.un("clear", this.refresh);
10762             this.store.un("beforeload", this.onBeforeLoad);
10763             this.store.un("load", this.onLoad);
10764             this.store.un("loadexception", this.onLoad);
10765         }
10766         if(store){
10767           
10768             store.on("datachanged", this.refresh, this);
10769             store.on("add", this.onAdd, this);
10770             store.on("remove", this.onRemove, this);
10771             store.on("update", this.onUpdate, this);
10772             store.on("clear", this.refresh, this);
10773             store.on("beforeload", this.onBeforeLoad, this);
10774             store.on("load", this.onLoad, this);
10775             store.on("loadexception", this.onLoad, this);
10776         }
10777         
10778         if(store){
10779             this.refresh();
10780         }
10781     },
10782     /**
10783      * onbeforeLoad - masks the loading area.
10784      *
10785      */
10786     onBeforeLoad : function(store,opts)
10787     {
10788          Roo.log('onBeforeLoad');   
10789         if (!opts.add) {
10790             this.el.update("");
10791         }
10792         this.el.mask(this.mask ? this.mask : "Loading" ); 
10793     },
10794     onLoad : function ()
10795     {
10796         this.el.unmask();
10797     },
10798     
10799
10800     /**
10801      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
10802      * @param {HTMLElement} node
10803      * @return {HTMLElement} The template node
10804      */
10805     findItemFromChild : function(node){
10806         var el = this.dataName  ?
10807             this.el.child('.roo-tpl-' + this.dataName,true) :
10808             this.el.dom; 
10809         
10810         if(!node || node.parentNode == el){
10811                     return node;
10812             }
10813             var p = node.parentNode;
10814             while(p && p != el){
10815             if(p.parentNode == el){
10816                 return p;
10817             }
10818             p = p.parentNode;
10819         }
10820             return null;
10821     },
10822
10823     /** @ignore */
10824     onClick : function(e){
10825         var item = this.findItemFromChild(e.getTarget());
10826         if(item){
10827             var index = this.indexOf(item);
10828             if(this.onItemClick(item, index, e) !== false){
10829                 this.fireEvent("click", this, index, item, e);
10830             }
10831         }else{
10832             this.clearSelections();
10833         }
10834     },
10835
10836     /** @ignore */
10837     onContextMenu : function(e){
10838         var item = this.findItemFromChild(e.getTarget());
10839         if(item){
10840             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
10841         }
10842     },
10843
10844     /** @ignore */
10845     onDblClick : function(e){
10846         var item = this.findItemFromChild(e.getTarget());
10847         if(item){
10848             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
10849         }
10850     },
10851
10852     onItemClick : function(item, index, e)
10853     {
10854         if(this.fireEvent("beforeclick", this, index, item, e) === false){
10855             return false;
10856         }
10857         if (this.toggleSelect) {
10858             var m = this.isSelected(item) ? 'unselect' : 'select';
10859             Roo.log(m);
10860             var _t = this;
10861             _t[m](item, true, false);
10862             return true;
10863         }
10864         if(this.multiSelect || this.singleSelect){
10865             if(this.multiSelect && e.shiftKey && this.lastSelection){
10866                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
10867             }else{
10868                 this.select(item, this.multiSelect && e.ctrlKey);
10869                 this.lastSelection = item;
10870             }
10871             e.preventDefault();
10872         }
10873         return true;
10874     },
10875
10876     /**
10877      * Get the number of selected nodes.
10878      * @return {Number}
10879      */
10880     getSelectionCount : function(){
10881         return this.selections.length;
10882     },
10883
10884     /**
10885      * Get the currently selected nodes.
10886      * @return {Array} An array of HTMLElements
10887      */
10888     getSelectedNodes : function(){
10889         return this.selections;
10890     },
10891
10892     /**
10893      * Get the indexes of the selected nodes.
10894      * @return {Array}
10895      */
10896     getSelectedIndexes : function(){
10897         var indexes = [], s = this.selections;
10898         for(var i = 0, len = s.length; i < len; i++){
10899             indexes.push(s[i].nodeIndex);
10900         }
10901         return indexes;
10902     },
10903
10904     /**
10905      * Clear all selections
10906      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
10907      */
10908     clearSelections : function(suppressEvent){
10909         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
10910             this.cmp.elements = this.selections;
10911             this.cmp.removeClass(this.selectedClass);
10912             this.selections = [];
10913             if(!suppressEvent){
10914                 this.fireEvent("selectionchange", this, this.selections);
10915             }
10916         }
10917     },
10918
10919     /**
10920      * Returns true if the passed node is selected
10921      * @param {HTMLElement/Number} node The node or node index
10922      * @return {Boolean}
10923      */
10924     isSelected : function(node){
10925         var s = this.selections;
10926         if(s.length < 1){
10927             return false;
10928         }
10929         node = this.getNode(node);
10930         return s.indexOf(node) !== -1;
10931     },
10932
10933     /**
10934      * Selects nodes.
10935      * @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
10936      * @param {Boolean} keepExisting (optional) true to keep existing selections
10937      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
10938      */
10939     select : function(nodeInfo, keepExisting, suppressEvent){
10940         if(nodeInfo instanceof Array){
10941             if(!keepExisting){
10942                 this.clearSelections(true);
10943             }
10944             for(var i = 0, len = nodeInfo.length; i < len; i++){
10945                 this.select(nodeInfo[i], true, true);
10946             }
10947             return;
10948         } 
10949         var node = this.getNode(nodeInfo);
10950         if(!node || this.isSelected(node)){
10951             return; // already selected.
10952         }
10953         if(!keepExisting){
10954             this.clearSelections(true);
10955         }
10956         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
10957             Roo.fly(node).addClass(this.selectedClass);
10958             this.selections.push(node);
10959             if(!suppressEvent){
10960                 this.fireEvent("selectionchange", this, this.selections);
10961             }
10962         }
10963         
10964         
10965     },
10966       /**
10967      * Unselects nodes.
10968      * @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
10969      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
10970      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
10971      */
10972     unselect : function(nodeInfo, keepExisting, suppressEvent)
10973     {
10974         if(nodeInfo instanceof Array){
10975             Roo.each(this.selections, function(s) {
10976                 this.unselect(s, nodeInfo);
10977             }, this);
10978             return;
10979         }
10980         var node = this.getNode(nodeInfo);
10981         if(!node || !this.isSelected(node)){
10982             Roo.log("not selected");
10983             return; // not selected.
10984         }
10985         // fireevent???
10986         var ns = [];
10987         Roo.each(this.selections, function(s) {
10988             if (s == node ) {
10989                 Roo.fly(node).removeClass(this.selectedClass);
10990
10991                 return;
10992             }
10993             ns.push(s);
10994         },this);
10995         
10996         this.selections= ns;
10997         this.fireEvent("selectionchange", this, this.selections);
10998     },
10999
11000     /**
11001      * Gets a template node.
11002      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
11003      * @return {HTMLElement} The node or null if it wasn't found
11004      */
11005     getNode : function(nodeInfo){
11006         if(typeof nodeInfo == "string"){
11007             return document.getElementById(nodeInfo);
11008         }else if(typeof nodeInfo == "number"){
11009             return this.nodes[nodeInfo];
11010         }
11011         return nodeInfo;
11012     },
11013
11014     /**
11015      * Gets a range template nodes.
11016      * @param {Number} startIndex
11017      * @param {Number} endIndex
11018      * @return {Array} An array of nodes
11019      */
11020     getNodes : function(start, end){
11021         var ns = this.nodes;
11022         start = start || 0;
11023         end = typeof end == "undefined" ? ns.length - 1 : end;
11024         var nodes = [];
11025         if(start <= end){
11026             for(var i = start; i <= end; i++){
11027                 nodes.push(ns[i]);
11028             }
11029         } else{
11030             for(var i = start; i >= end; i--){
11031                 nodes.push(ns[i]);
11032             }
11033         }
11034         return nodes;
11035     },
11036
11037     /**
11038      * Finds the index of the passed node
11039      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
11040      * @return {Number} The index of the node or -1
11041      */
11042     indexOf : function(node){
11043         node = this.getNode(node);
11044         if(typeof node.nodeIndex == "number"){
11045             return node.nodeIndex;
11046         }
11047         var ns = this.nodes;
11048         for(var i = 0, len = ns.length; i < len; i++){
11049             if(ns[i] == node){
11050                 return i;
11051             }
11052         }
11053         return -1;
11054     }
11055 });
11056 /*
11057  * - LGPL
11058  *
11059  * based on jquery fullcalendar
11060  * 
11061  */
11062
11063 Roo.bootstrap = Roo.bootstrap || {};
11064 /**
11065  * @class Roo.bootstrap.Calendar
11066  * @extends Roo.bootstrap.Component
11067  * Bootstrap Calendar class
11068  * @cfg {Boolean} loadMask (true|false) default false
11069  * @cfg {Object} header generate the user specific header of the calendar, default false
11070
11071  * @constructor
11072  * Create a new Container
11073  * @param {Object} config The config object
11074  */
11075
11076
11077
11078 Roo.bootstrap.Calendar = function(config){
11079     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
11080      this.addEvents({
11081         /**
11082              * @event select
11083              * Fires when a date is selected
11084              * @param {DatePicker} this
11085              * @param {Date} date The selected date
11086              */
11087         'select': true,
11088         /**
11089              * @event monthchange
11090              * Fires when the displayed month changes 
11091              * @param {DatePicker} this
11092              * @param {Date} date The selected month
11093              */
11094         'monthchange': true,
11095         /**
11096              * @event evententer
11097              * Fires when mouse over an event
11098              * @param {Calendar} this
11099              * @param {event} Event
11100              */
11101         'evententer': true,
11102         /**
11103              * @event eventleave
11104              * Fires when the mouse leaves an
11105              * @param {Calendar} this
11106              * @param {event}
11107              */
11108         'eventleave': true,
11109         /**
11110              * @event eventclick
11111              * Fires when the mouse click an
11112              * @param {Calendar} this
11113              * @param {event}
11114              */
11115         'eventclick': true
11116         
11117     });
11118
11119 };
11120
11121 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
11122     
11123      /**
11124      * @cfg {Number} startDay
11125      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
11126      */
11127     startDay : 0,
11128     
11129     loadMask : false,
11130     
11131     header : false,
11132       
11133     getAutoCreate : function(){
11134         
11135         
11136         var fc_button = function(name, corner, style, content ) {
11137             return Roo.apply({},{
11138                 tag : 'span',
11139                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
11140                          (corner.length ?
11141                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
11142                             ''
11143                         ),
11144                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
11145                 unselectable: 'on'
11146             });
11147         };
11148         
11149         var header = {};
11150         
11151         if(!this.header){
11152             header = {
11153                 tag : 'table',
11154                 cls : 'fc-header',
11155                 style : 'width:100%',
11156                 cn : [
11157                     {
11158                         tag: 'tr',
11159                         cn : [
11160                             {
11161                                 tag : 'td',
11162                                 cls : 'fc-header-left',
11163                                 cn : [
11164                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
11165                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
11166                                     { tag: 'span', cls: 'fc-header-space' },
11167                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
11168
11169
11170                                 ]
11171                             },
11172
11173                             {
11174                                 tag : 'td',
11175                                 cls : 'fc-header-center',
11176                                 cn : [
11177                                     {
11178                                         tag: 'span',
11179                                         cls: 'fc-header-title',
11180                                         cn : {
11181                                             tag: 'H2',
11182                                             html : 'month / year'
11183                                         }
11184                                     }
11185
11186                                 ]
11187                             },
11188                             {
11189                                 tag : 'td',
11190                                 cls : 'fc-header-right',
11191                                 cn : [
11192                               /*      fc_button('month', 'left', '', 'month' ),
11193                                     fc_button('week', '', '', 'week' ),
11194                                     fc_button('day', 'right', '', 'day' )
11195                                 */    
11196
11197                                 ]
11198                             }
11199
11200                         ]
11201                     }
11202                 ]
11203             };
11204         }
11205         
11206         header = this.header;
11207         
11208        
11209         var cal_heads = function() {
11210             var ret = [];
11211             // fixme - handle this.
11212             
11213             for (var i =0; i < Date.dayNames.length; i++) {
11214                 var d = Date.dayNames[i];
11215                 ret.push({
11216                     tag: 'th',
11217                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
11218                     html : d.substring(0,3)
11219                 });
11220                 
11221             }
11222             ret[0].cls += ' fc-first';
11223             ret[6].cls += ' fc-last';
11224             return ret;
11225         };
11226         var cal_cell = function(n) {
11227             return  {
11228                 tag: 'td',
11229                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
11230                 cn : [
11231                     {
11232                         cn : [
11233                             {
11234                                 cls: 'fc-day-number',
11235                                 html: 'D'
11236                             },
11237                             {
11238                                 cls: 'fc-day-content',
11239                              
11240                                 cn : [
11241                                      {
11242                                         style: 'position: relative;' // height: 17px;
11243                                     }
11244                                 ]
11245                             }
11246                             
11247                             
11248                         ]
11249                     }
11250                 ]
11251                 
11252             }
11253         };
11254         var cal_rows = function() {
11255             
11256             var ret = []
11257             for (var r = 0; r < 6; r++) {
11258                 var row= {
11259                     tag : 'tr',
11260                     cls : 'fc-week',
11261                     cn : []
11262                 };
11263                 
11264                 for (var i =0; i < Date.dayNames.length; i++) {
11265                     var d = Date.dayNames[i];
11266                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
11267
11268                 }
11269                 row.cn[0].cls+=' fc-first';
11270                 row.cn[0].cn[0].style = 'min-height:90px';
11271                 row.cn[6].cls+=' fc-last';
11272                 ret.push(row);
11273                 
11274             }
11275             ret[0].cls += ' fc-first';
11276             ret[4].cls += ' fc-prev-last';
11277             ret[5].cls += ' fc-last';
11278             return ret;
11279             
11280         };
11281         
11282         var cal_table = {
11283             tag: 'table',
11284             cls: 'fc-border-separate',
11285             style : 'width:100%',
11286             cellspacing  : 0,
11287             cn : [
11288                 { 
11289                     tag: 'thead',
11290                     cn : [
11291                         { 
11292                             tag: 'tr',
11293                             cls : 'fc-first fc-last',
11294                             cn : cal_heads()
11295                         }
11296                     ]
11297                 },
11298                 { 
11299                     tag: 'tbody',
11300                     cn : cal_rows()
11301                 }
11302                   
11303             ]
11304         };
11305          
11306          var cfg = {
11307             cls : 'fc fc-ltr',
11308             cn : [
11309                 header,
11310                 {
11311                     cls : 'fc-content',
11312                     style : "position: relative;",
11313                     cn : [
11314                         {
11315                             cls : 'fc-view fc-view-month fc-grid',
11316                             style : 'position: relative',
11317                             unselectable : 'on',
11318                             cn : [
11319                                 {
11320                                     cls : 'fc-event-container',
11321                                     style : 'position:absolute;z-index:8;top:0;left:0;'
11322                                 },
11323                                 cal_table
11324                             ]
11325                         }
11326                     ]
11327     
11328                 }
11329            ] 
11330             
11331         };
11332         
11333          
11334         
11335         return cfg;
11336     },
11337     
11338     
11339     initEvents : function()
11340     {
11341         if(!this.store){
11342             throw "can not find store for calendar";
11343         }
11344         
11345         var mark = {
11346             tag: "div",
11347             cls:"x-dlg-mask",
11348             style: "text-align:center",
11349             cn: [
11350                 {
11351                     tag: "div",
11352                     style: "background-color:white;width:50%;margin:250 auto",
11353                     cn: [
11354                         {
11355                             tag: "img",
11356                             src: rootURL + '/roojs1/images/ux/lightbox/loading.gif'
11357                         },
11358                         {
11359                             tag: "span",
11360                             html: "Loading"
11361                         }
11362                         
11363                     ]
11364                 }
11365             ]
11366         }
11367         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
11368         
11369         var size = this.el.select('.fc-content', true).first().getSize();
11370         this.maskEl.setSize(size.width, size.height);
11371         this.maskEl.enableDisplayMode("block");
11372         if(!this.loadMask){
11373             this.maskEl.hide();
11374         }
11375         
11376         this.store = Roo.factory(this.store, Roo.data);
11377         this.store.on('load', this.onLoad, this);
11378         this.store.on('beforeload', this.onBeforeLoad, this);
11379         
11380         this.resize();
11381         
11382         this.cells = this.el.select('.fc-day',true);
11383         //Roo.log(this.cells);
11384         this.textNodes = this.el.query('.fc-day-number');
11385         this.cells.addClassOnOver('fc-state-hover');
11386         
11387         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
11388         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
11389         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
11390         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
11391         
11392         this.on('monthchange', this.onMonthChange, this);
11393         
11394         this.update(new Date().clearTime());
11395     },
11396     
11397     resize : function() {
11398         var sz  = this.el.getSize();
11399         
11400         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
11401         this.el.select('.fc-day-content div',true).setHeight(34);
11402     },
11403     
11404     
11405     // private
11406     showPrevMonth : function(e){
11407         this.update(this.activeDate.add("mo", -1));
11408     },
11409     showToday : function(e){
11410         this.update(new Date().clearTime());
11411     },
11412     // private
11413     showNextMonth : function(e){
11414         this.update(this.activeDate.add("mo", 1));
11415     },
11416
11417     // private
11418     showPrevYear : function(){
11419         this.update(this.activeDate.add("y", -1));
11420     },
11421
11422     // private
11423     showNextYear : function(){
11424         this.update(this.activeDate.add("y", 1));
11425     },
11426
11427     
11428    // private
11429     update : function(date)
11430     {
11431         var vd = this.activeDate;
11432         this.activeDate = date;
11433 //        if(vd && this.el){
11434 //            var t = date.getTime();
11435 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
11436 //                Roo.log('using add remove');
11437 //                
11438 //                this.fireEvent('monthchange', this, date);
11439 //                
11440 //                this.cells.removeClass("fc-state-highlight");
11441 //                this.cells.each(function(c){
11442 //                   if(c.dateValue == t){
11443 //                       c.addClass("fc-state-highlight");
11444 //                       setTimeout(function(){
11445 //                            try{c.dom.firstChild.focus();}catch(e){}
11446 //                       }, 50);
11447 //                       return false;
11448 //                   }
11449 //                   return true;
11450 //                });
11451 //                return;
11452 //            }
11453 //        }
11454         
11455         var days = date.getDaysInMonth();
11456         
11457         var firstOfMonth = date.getFirstDateOfMonth();
11458         var startingPos = firstOfMonth.getDay()-this.startDay;
11459         
11460         if(startingPos < this.startDay){
11461             startingPos += 7;
11462         }
11463         
11464         var pm = date.add(Date.MONTH, -1);
11465         var prevStart = pm.getDaysInMonth()-startingPos;
11466 //        
11467         this.cells = this.el.select('.fc-day',true);
11468         this.textNodes = this.el.query('.fc-day-number');
11469         this.cells.addClassOnOver('fc-state-hover');
11470         
11471         var cells = this.cells.elements;
11472         var textEls = this.textNodes;
11473         
11474         Roo.each(cells, function(cell){
11475             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
11476         });
11477         
11478         days += startingPos;
11479
11480         // convert everything to numbers so it's fast
11481         var day = 86400000;
11482         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
11483         //Roo.log(d);
11484         //Roo.log(pm);
11485         //Roo.log(prevStart);
11486         
11487         var today = new Date().clearTime().getTime();
11488         var sel = date.clearTime().getTime();
11489         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
11490         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
11491         var ddMatch = this.disabledDatesRE;
11492         var ddText = this.disabledDatesText;
11493         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
11494         var ddaysText = this.disabledDaysText;
11495         var format = this.format;
11496         
11497         var setCellClass = function(cal, cell){
11498             
11499             //Roo.log('set Cell Class');
11500             cell.title = "";
11501             var t = d.getTime();
11502             
11503             //Roo.log(d);
11504             
11505             cell.dateValue = t;
11506             if(t == today){
11507                 cell.className += " fc-today";
11508                 cell.className += " fc-state-highlight";
11509                 cell.title = cal.todayText;
11510             }
11511             if(t == sel){
11512                 // disable highlight in other month..
11513                 //cell.className += " fc-state-highlight";
11514                 
11515             }
11516             // disabling
11517             if(t < min) {
11518                 cell.className = " fc-state-disabled";
11519                 cell.title = cal.minText;
11520                 return;
11521             }
11522             if(t > max) {
11523                 cell.className = " fc-state-disabled";
11524                 cell.title = cal.maxText;
11525                 return;
11526             }
11527             if(ddays){
11528                 if(ddays.indexOf(d.getDay()) != -1){
11529                     cell.title = ddaysText;
11530                     cell.className = " fc-state-disabled";
11531                 }
11532             }
11533             if(ddMatch && format){
11534                 var fvalue = d.dateFormat(format);
11535                 if(ddMatch.test(fvalue)){
11536                     cell.title = ddText.replace("%0", fvalue);
11537                     cell.className = " fc-state-disabled";
11538                 }
11539             }
11540             
11541             if (!cell.initialClassName) {
11542                 cell.initialClassName = cell.dom.className;
11543             }
11544             
11545             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
11546         };
11547
11548         var i = 0;
11549         
11550         for(; i < startingPos; i++) {
11551             textEls[i].innerHTML = (++prevStart);
11552             d.setDate(d.getDate()+1);
11553             
11554             cells[i].className = "fc-past fc-other-month";
11555             setCellClass(this, cells[i]);
11556         }
11557         
11558         var intDay = 0;
11559         
11560         for(; i < days; i++){
11561             intDay = i - startingPos + 1;
11562             textEls[i].innerHTML = (intDay);
11563             d.setDate(d.getDate()+1);
11564             
11565             cells[i].className = ''; // "x-date-active";
11566             setCellClass(this, cells[i]);
11567         }
11568         var extraDays = 0;
11569         
11570         for(; i < 42; i++) {
11571             textEls[i].innerHTML = (++extraDays);
11572             d.setDate(d.getDate()+1);
11573             
11574             cells[i].className = "fc-future fc-other-month";
11575             setCellClass(this, cells[i]);
11576         }
11577         
11578         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
11579         
11580         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
11581         
11582         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
11583         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
11584         
11585         if(totalRows != 6){
11586             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
11587             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
11588         }
11589         
11590         this.fireEvent('monthchange', this, date);
11591         
11592         
11593         /*
11594         if(!this.internalRender){
11595             var main = this.el.dom.firstChild;
11596             var w = main.offsetWidth;
11597             this.el.setWidth(w + this.el.getBorderWidth("lr"));
11598             Roo.fly(main).setWidth(w);
11599             this.internalRender = true;
11600             // opera does not respect the auto grow header center column
11601             // then, after it gets a width opera refuses to recalculate
11602             // without a second pass
11603             if(Roo.isOpera && !this.secondPass){
11604                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
11605                 this.secondPass = true;
11606                 this.update.defer(10, this, [date]);
11607             }
11608         }
11609         */
11610         
11611     },
11612     
11613     findCell : function(dt) {
11614         dt = dt.clearTime().getTime();
11615         var ret = false;
11616         this.cells.each(function(c){
11617             //Roo.log("check " +c.dateValue + '?=' + dt);
11618             if(c.dateValue == dt){
11619                 ret = c;
11620                 return false;
11621             }
11622             return true;
11623         });
11624         
11625         return ret;
11626     },
11627     
11628     findCells : function(ev) {
11629         var s = ev.start.clone().clearTime().getTime();
11630        // Roo.log(s);
11631         var e= ev.end.clone().clearTime().getTime();
11632        // Roo.log(e);
11633         var ret = [];
11634         this.cells.each(function(c){
11635              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
11636             
11637             if(c.dateValue > e){
11638                 return ;
11639             }
11640             if(c.dateValue < s){
11641                 return ;
11642             }
11643             ret.push(c);
11644         });
11645         
11646         return ret;    
11647     },
11648     
11649 //    findBestRow: function(cells)
11650 //    {
11651 //        var ret = 0;
11652 //        
11653 //        for (var i =0 ; i < cells.length;i++) {
11654 //            ret  = Math.max(cells[i].rows || 0,ret);
11655 //        }
11656 //        return ret;
11657 //        
11658 //    },
11659     
11660     
11661     addItem : function(ev)
11662     {
11663         // look for vertical location slot in
11664         var cells = this.findCells(ev);
11665         
11666 //        ev.row = this.findBestRow(cells);
11667         
11668         // work out the location.
11669         
11670         var crow = false;
11671         var rows = [];
11672         for(var i =0; i < cells.length; i++) {
11673             if (!crow) {
11674                 crow = {
11675                     start : cells[i],
11676                     end :  cells[i]
11677                 };
11678                 continue;
11679             }
11680             if (crow.start.getY() == cells[i].getY()) {
11681                 // on same row.
11682                 crow.end = cells[i];
11683                 continue;
11684             }
11685             // different row.
11686             rows.push(crow);
11687             crow = {
11688                 start: cells[i],
11689                 end : cells[i]
11690             };
11691             
11692         }
11693         
11694         rows.push(crow);
11695         ev.els = [];
11696         ev.rows = rows;
11697         ev.cells = cells;
11698         ev.rendered = false;
11699 //        for (var i = 0; i < cells.length;i++) {
11700 //            cells[i].rows = Math.max(cells[i].rows || 0 , ev.row + 1 );
11701 //            
11702 //        }
11703         
11704         this.calevents.push(ev);
11705     },
11706     
11707     clearEvents: function() {
11708         
11709         if(!this.calevents){
11710             return;
11711         }
11712         
11713         Roo.each(this.cells.elements, function(c){
11714             c.rows = [];
11715             c.more = [];
11716         });
11717         
11718         Roo.each(this.calevents, function(e) {
11719             Roo.each(e.els, function(el) {
11720                 el.un('mouseenter' ,this.onEventEnter, this);
11721                 el.un('mouseleave' ,this.onEventLeave, this);
11722                 el.remove();
11723             },this);
11724         },this);
11725         
11726         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
11727             e.remove();
11728         });
11729         
11730     },
11731     
11732     renderEvents: function()
11733     {   
11734         // first make sure there is enough space..
11735         this.cells.each(function(c) {
11736             c.rows = [];
11737             c.more = [];
11738         });
11739         
11740         for (var e = 0; e < this.calevents.length; e++) {
11741             
11742             var ev = this.calevents[e];
11743             var cells = ev.cells;
11744             var rows = ev.rows;
11745             
11746             for(var i = 0; i < cells.length; i++){
11747                 
11748                 var cbox = this.cells.item(this.cells.indexOf(cells[i]));
11749                 
11750                 if(cells.length < 2 && cbox.rows.length > 3){
11751                     cbox.more.push(ev);
11752                     continue;
11753                 }
11754                 
11755                 cbox.rows.push(ev);
11756             }
11757         }
11758         
11759         var _this = this;
11760         
11761         this.cells.each(function(c) {
11762             if(c.more.length && c.more.length == 1){
11763                 c.rows.push(c.more.pop());
11764             }
11765             
11766             var r = (c.more.length) ? c.rows.length + 1 : c.rows.length;
11767             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, r * 20));
11768             
11769             
11770             for (var e = 0; e < c.rows.length; e++){
11771                 var ev = c.rows[e];
11772                 
11773                 if(ev.rendered){
11774                     continue;
11775                 }
11776                 
11777                 var cells = ev.cells;
11778                 var rows = ev.rows;
11779                 
11780                 for(var i = 0; i < rows.length; i++) {
11781                 
11782                     // how many rows should it span..
11783
11784                     var  cfg = {
11785                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
11786                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
11787
11788                         unselectable : "on",
11789                         cn : [
11790                             {
11791                                 cls: 'fc-event-inner',
11792                                 cn : [
11793     //                                {
11794     //                                  tag:'span',
11795     //                                  cls: 'fc-event-time',
11796     //                                  html : cells.length > 1 ? '' : ev.time
11797     //                                },
11798                                     {
11799                                       tag:'span',
11800                                       cls: 'fc-event-title',
11801                                       html : String.format('{0}', ev.title)
11802                                     }
11803
11804
11805                                 ]
11806                             },
11807                             {
11808                                 cls: 'ui-resizable-handle ui-resizable-e',
11809                                 html : '&nbsp;&nbsp;&nbsp'
11810                             }
11811
11812                         ]
11813                     };
11814
11815                     if (i == 0) {
11816                         cfg.cls += ' fc-event-start';
11817                     }
11818                     if ((i+1) == rows.length) {
11819                         cfg.cls += ' fc-event-end';
11820                     }
11821
11822                     var ctr = _this.el.select('.fc-event-container',true).first();
11823                     var cg = ctr.createChild(cfg);
11824
11825                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
11826                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
11827
11828                     cg.setXY([sbox.x +2, sbox.y +(e * 20)]);    
11829                     cg.setWidth(ebox.right - sbox.x -2);
11830
11831                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
11832                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
11833                     cg.on('click', _this.onEventClick, _this, ev);
11834
11835                     ev.els.push(cg);
11836                     
11837                     ev.rendered = true;
11838                 }
11839                 
11840             }
11841             
11842             
11843             if(c.more.length){
11844                 var  cfg = {
11845                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
11846                     style : 'position: absolute',
11847                     unselectable : "on",
11848                     cn : [
11849                         {
11850                             cls: 'fc-event-inner',
11851                             cn : [
11852                                 {
11853                                   tag:'span',
11854                                   cls: 'fc-event-title',
11855                                   html : 'More'
11856                                 }
11857
11858
11859                             ]
11860                         },
11861                         {
11862                             cls: 'ui-resizable-handle ui-resizable-e',
11863                             html : '&nbsp;&nbsp;&nbsp'
11864                         }
11865
11866                     ]
11867                 };
11868
11869                 var ctr = _this.el.select('.fc-event-container',true).first();
11870                 var cg = ctr.createChild(cfg);
11871
11872                 var sbox = c.select('.fc-day-content',true).first().getBox();
11873                 var ebox = c.select('.fc-day-content',true).first().getBox();
11874                 //Roo.log(cg);
11875                 cg.setXY([sbox.x +2, sbox.y +(c.rows.length * 20)]);    
11876                 cg.setWidth(ebox.right - sbox.x -2);
11877
11878                 cg.on('click', _this.onMoreEventClick, _this, c.more);
11879                 
11880             }
11881             
11882         });
11883         
11884         
11885         
11886     },
11887     
11888     onEventEnter: function (e, el,event,d) {
11889         this.fireEvent('evententer', this, el, event);
11890     },
11891     
11892     onEventLeave: function (e, el,event,d) {
11893         this.fireEvent('eventleave', this, el, event);
11894     },
11895     
11896     onEventClick: function (e, el,event,d) {
11897         this.fireEvent('eventclick', this, el, event);
11898     },
11899     
11900     onMonthChange: function () {
11901         this.store.load();
11902     },
11903     
11904     onMoreEventClick: function(e, el, more)
11905     {
11906         var _this = this;
11907         
11908         this.calpopover.placement = 'right';
11909         this.calpopover.setTitle('More');
11910         
11911         this.calpopover.setContent('');
11912         
11913         var ctr = this.calpopover.el.select('.popover-content', true).first();
11914         
11915         Roo.each(more, function(m){
11916             var cfg = {
11917                 cls : 'fc-event-hori fc-event-draggable',
11918                 html : m.title
11919             }
11920             var cg = ctr.createChild(cfg);
11921             
11922             cg.on('click', _this.onEventClick, _this, m);
11923         });
11924         
11925         this.calpopover.show(el);
11926         
11927         
11928     },
11929     
11930     onLoad: function () 
11931     {   
11932         this.calevents = [];
11933         var cal = this;
11934         
11935         if(this.store.getCount() > 0){
11936             this.store.data.each(function(d){
11937                cal.addItem({
11938                     id : d.data.id,
11939                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
11940                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
11941                     time : d.data.start_time,
11942                     title : d.data.title,
11943                     description : d.data.description,
11944                     venue : d.data.venue
11945                 });
11946             });
11947         }
11948         
11949         this.renderEvents();
11950         
11951         if(this.calevents.length && this.loadMask){
11952             this.maskEl.hide();
11953         }
11954     },
11955     
11956     onBeforeLoad: function()
11957     {
11958         this.clearEvents();
11959         if(this.loadMask){
11960             this.maskEl.show();
11961         }
11962     }
11963 });
11964
11965  
11966  /*
11967  * - LGPL
11968  *
11969  * element
11970  * 
11971  */
11972
11973 /**
11974  * @class Roo.bootstrap.Popover
11975  * @extends Roo.bootstrap.Component
11976  * Bootstrap Popover class
11977  * @cfg {String} html contents of the popover   (or false to use children..)
11978  * @cfg {String} title of popover (or false to hide)
11979  * @cfg {String} placement how it is placed
11980  * @cfg {String} trigger click || hover (or false to trigger manually)
11981  * @cfg {String} over what (parent or false to trigger manually.)
11982  * 
11983  * @constructor
11984  * Create a new Popover
11985  * @param {Object} config The config object
11986  */
11987
11988 Roo.bootstrap.Popover = function(config){
11989     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
11990 };
11991
11992 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
11993     
11994     title: 'Fill in a title',
11995     html: false,
11996     
11997     placement : 'right',
11998     trigger : 'hover', // hover
11999     
12000     over: 'parent',
12001     
12002     can_build_overlaid : false,
12003     
12004     getChildContainer : function()
12005     {
12006         return this.el.select('.popover-content',true).first();
12007     },
12008     
12009     getAutoCreate : function(){
12010          Roo.log('make popover?');
12011         var cfg = {
12012            cls : 'popover roo-dynamic',
12013            style: 'display:block',
12014            cn : [
12015                 {
12016                     cls : 'arrow'
12017                 },
12018                 {
12019                     cls : 'popover-inner',
12020                     cn : [
12021                         {
12022                             tag: 'h3',
12023                             cls: 'popover-title',
12024                             html : this.title
12025                         },
12026                         {
12027                             cls : 'popover-content',
12028                             html : this.html
12029                         }
12030                     ]
12031                     
12032                 }
12033            ]
12034         };
12035         
12036         return cfg;
12037     },
12038     setTitle: function(str)
12039     {
12040         this.el.select('.popover-title',true).first().dom.innerHTML = str;
12041     },
12042     setContent: function(str)
12043     {
12044         this.el.select('.popover-content',true).first().dom.innerHTML = str;
12045     },
12046     // as it get's added to the bottom of the page.
12047     onRender : function(ct, position)
12048     {
12049         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
12050         if(!this.el){
12051             var cfg = Roo.apply({},  this.getAutoCreate());
12052             cfg.id = Roo.id();
12053             
12054             if (this.cls) {
12055                 cfg.cls += ' ' + this.cls;
12056             }
12057             if (this.style) {
12058                 cfg.style = this.style;
12059             }
12060             Roo.log("adding to ")
12061             this.el = Roo.get(document.body).createChild(cfg, position);
12062             Roo.log(this.el);
12063         }
12064         this.initEvents();
12065     },
12066     
12067     initEvents : function()
12068     {
12069         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
12070         this.el.enableDisplayMode('block');
12071         this.el.hide();
12072         if (this.over === false) {
12073             return; 
12074         }
12075         if (this.triggers === false) {
12076             return;
12077         }
12078         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
12079         var triggers = this.trigger ? this.trigger.split(' ') : [];
12080         Roo.each(triggers, function(trigger) {
12081         
12082             if (trigger == 'click') {
12083                 on_el.on('click', this.toggle, this);
12084             } else if (trigger != 'manual') {
12085                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
12086                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
12087       
12088                 on_el.on(eventIn  ,this.enter, this);
12089                 on_el.on(eventOut, this.leave, this);
12090             }
12091         }, this);
12092         
12093     },
12094     
12095     
12096     // private
12097     timeout : null,
12098     hoverState : null,
12099     
12100     toggle : function () {
12101         this.hoverState == 'in' ? this.leave() : this.enter();
12102     },
12103     
12104     enter : function () {
12105        
12106     
12107         clearTimeout(this.timeout);
12108     
12109         this.hoverState = 'in'
12110     
12111         if (!this.delay || !this.delay.show) {
12112             this.show();
12113             return 
12114         }
12115         var _t = this;
12116         this.timeout = setTimeout(function () {
12117             if (_t.hoverState == 'in') {
12118                 _t.show();
12119             }
12120         }, this.delay.show)
12121     },
12122     leave : function() {
12123         clearTimeout(this.timeout);
12124     
12125         this.hoverState = 'out'
12126     
12127         if (!this.delay || !this.delay.hide) {
12128             this.hide();
12129             return 
12130         }
12131         var _t = this;
12132         this.timeout = setTimeout(function () {
12133             if (_t.hoverState == 'out') {
12134                 _t.hide();
12135             }
12136         }, this.delay.hide)
12137     },
12138     
12139     show : function (on_el)
12140     {
12141         if (!on_el) {
12142             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
12143         }
12144         // set content.
12145         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
12146         if (this.html !== false) {
12147             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
12148         }
12149         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
12150         if (!this.title.length) {
12151             this.el.select('.popover-title',true).hide();
12152         }
12153         
12154         var placement = typeof this.placement == 'function' ?
12155             this.placement.call(this, this.el, on_el) :
12156             this.placement;
12157             
12158         var autoToken = /\s?auto?\s?/i;
12159         var autoPlace = autoToken.test(placement);
12160         if (autoPlace) {
12161             placement = placement.replace(autoToken, '') || 'top';
12162         }
12163         
12164         //this.el.detach()
12165         //this.el.setXY([0,0]);
12166         this.el.show();
12167         this.el.dom.style.display='block';
12168         this.el.addClass(placement);
12169         
12170         //this.el.appendTo(on_el);
12171         
12172         var p = this.getPosition();
12173         var box = this.el.getBox();
12174         
12175         if (autoPlace) {
12176             // fixme..
12177         }
12178         var align = Roo.bootstrap.Popover.alignment[placement]
12179         this.el.alignTo(on_el, align[0],align[1]);
12180         //var arrow = this.el.select('.arrow',true).first();
12181         //arrow.set(align[2], 
12182         
12183         this.el.addClass('in');
12184         this.hoverState = null;
12185         
12186         if (this.el.hasClass('fade')) {
12187             // fade it?
12188         }
12189         
12190     },
12191     hide : function()
12192     {
12193         this.el.setXY([0,0]);
12194         this.el.removeClass('in');
12195         this.el.hide();
12196         
12197     }
12198     
12199 });
12200
12201 Roo.bootstrap.Popover.alignment = {
12202     'left' : ['r-l', [-10,0], 'right'],
12203     'right' : ['l-r', [10,0], 'left'],
12204     'bottom' : ['t-b', [0,10], 'top'],
12205     'top' : [ 'b-t', [0,-10], 'bottom']
12206 };
12207
12208  /*
12209  * - LGPL
12210  *
12211  * Progress
12212  * 
12213  */
12214
12215 /**
12216  * @class Roo.bootstrap.Progress
12217  * @extends Roo.bootstrap.Component
12218  * Bootstrap Progress class
12219  * @cfg {Boolean} striped striped of the progress bar
12220  * @cfg {Boolean} active animated of the progress bar
12221  * 
12222  * 
12223  * @constructor
12224  * Create a new Progress
12225  * @param {Object} config The config object
12226  */
12227
12228 Roo.bootstrap.Progress = function(config){
12229     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
12230 };
12231
12232 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
12233     
12234     striped : false,
12235     active: false,
12236     
12237     getAutoCreate : function(){
12238         var cfg = {
12239             tag: 'div',
12240             cls: 'progress'
12241         };
12242         
12243         
12244         if(this.striped){
12245             cfg.cls += ' progress-striped';
12246         }
12247       
12248         if(this.active){
12249             cfg.cls += ' active';
12250         }
12251         
12252         
12253         return cfg;
12254     }
12255    
12256 });
12257
12258  
12259
12260  /*
12261  * - LGPL
12262  *
12263  * ProgressBar
12264  * 
12265  */
12266
12267 /**
12268  * @class Roo.bootstrap.ProgressBar
12269  * @extends Roo.bootstrap.Component
12270  * Bootstrap ProgressBar class
12271  * @cfg {Number} aria_valuenow aria-value now
12272  * @cfg {Number} aria_valuemin aria-value min
12273  * @cfg {Number} aria_valuemax aria-value max
12274  * @cfg {String} label label for the progress bar
12275  * @cfg {String} panel (success | info | warning | danger )
12276  * @cfg {String} role role of the progress bar
12277  * @cfg {String} sr_only text
12278  * 
12279  * 
12280  * @constructor
12281  * Create a new ProgressBar
12282  * @param {Object} config The config object
12283  */
12284
12285 Roo.bootstrap.ProgressBar = function(config){
12286     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
12287 };
12288
12289 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
12290     
12291     aria_valuenow : 0,
12292     aria_valuemin : 0,
12293     aria_valuemax : 100,
12294     label : false,
12295     panel : false,
12296     role : false,
12297     sr_only: false,
12298     
12299     getAutoCreate : function()
12300     {
12301         
12302         var cfg = {
12303             tag: 'div',
12304             cls: 'progress-bar',
12305             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
12306         };
12307         
12308         if(this.sr_only){
12309             cfg.cn = {
12310                 tag: 'span',
12311                 cls: 'sr-only',
12312                 html: this.sr_only
12313             }
12314         }
12315         
12316         if(this.role){
12317             cfg.role = this.role;
12318         }
12319         
12320         if(this.aria_valuenow){
12321             cfg['aria-valuenow'] = this.aria_valuenow;
12322         }
12323         
12324         if(this.aria_valuemin){
12325             cfg['aria-valuemin'] = this.aria_valuemin;
12326         }
12327         
12328         if(this.aria_valuemax){
12329             cfg['aria-valuemax'] = this.aria_valuemax;
12330         }
12331         
12332         if(this.label && !this.sr_only){
12333             cfg.html = this.label;
12334         }
12335         
12336         if(this.panel){
12337             cfg.cls += ' progress-bar-' + this.panel;
12338         }
12339         
12340         return cfg;
12341     },
12342     
12343     update : function(aria_valuenow)
12344     {
12345         this.aria_valuenow = aria_valuenow;
12346         
12347         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
12348     }
12349    
12350 });
12351
12352  
12353
12354  /*
12355  * - LGPL
12356  *
12357  * TabPanel
12358  * 
12359  */
12360
12361 /**
12362  * @class Roo.bootstrap.TabPanel
12363  * @extends Roo.bootstrap.Component
12364  * Bootstrap TabPanel class
12365  * @cfg {Boolean} active panel active
12366  * @cfg {String} html panel content
12367  * @cfg {String} tabId tab relate id
12368  * @cfg {String} navId The navbar which triggers show hide
12369  * 
12370  * 
12371  * @constructor
12372  * Create a new TabPanel
12373  * @param {Object} config The config object
12374  */
12375
12376 Roo.bootstrap.TabPanel = function(config){
12377     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
12378      this.addEvents({
12379         /**
12380              * @event changed
12381              * Fires when the active status changes
12382              * @param {Roo.bootstrap.TabPanel} this
12383              * @param {Boolean} state the new state
12384             
12385          */
12386         'changed': true
12387      });
12388 };
12389
12390 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
12391     
12392     active: false,
12393     html: false,
12394     tabId: false,
12395     navId : false,
12396     
12397     getAutoCreate : function(){
12398         var cfg = {
12399             tag: 'div',
12400             cls: 'tab-pane',
12401             html: this.html || ''
12402         };
12403         
12404         if(this.active){
12405             cfg.cls += ' active';
12406         }
12407         
12408         if(this.tabId){
12409             cfg.tabId = this.tabId;
12410         }
12411         
12412         return cfg;
12413     },
12414     onRender : function(ct, position)
12415     {
12416        // Roo.log("Call onRender: " + this.xtype);
12417         
12418         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
12419         
12420         if (this.navId && this.tabId) {
12421             var item = Roo.bootstrap.NavGroup.get(this.navId).getNavItem(this.tabId);
12422             if (!item) {
12423                 Roo.log("could not find navID:"  + this.navId + ", tabId: " + this.tabId);
12424             } else {
12425                 item.on('changed', function(item, state) {
12426                     this.setActive(state);
12427                 }, this);
12428             }
12429         }
12430         
12431     },
12432     setActive: function(state)
12433     {
12434         Roo.log("panel - set active " + this.tabId + "=" + state);
12435         
12436         this.active = state;
12437         if (!state) {
12438             this.el.removeClass('active');
12439             
12440         } else  if (!this.el.hasClass('active')) {
12441             this.el.addClass('active');
12442         }
12443         this.fireEvent('changed', this, state);
12444     }
12445     
12446     
12447 });
12448  
12449
12450  
12451
12452  /*
12453  * - LGPL
12454  *
12455  * DateField
12456  * 
12457  */
12458
12459 /**
12460  * @class Roo.bootstrap.DateField
12461  * @extends Roo.bootstrap.Input
12462  * Bootstrap DateField class
12463  * @cfg {Number} weekStart default 0
12464  * @cfg {Number} weekStart default 0
12465  * @cfg {Number} viewMode default empty, (months|years)
12466  * @cfg {Number} minViewMode default empty, (months|years)
12467  * @cfg {Number} startDate default -Infinity
12468  * @cfg {Number} endDate default Infinity
12469  * @cfg {Boolean} todayHighlight default false
12470  * @cfg {Boolean} todayBtn default false
12471  * @cfg {Boolean} calendarWeeks default false
12472  * @cfg {Object} daysOfWeekDisabled default empty
12473  * 
12474  * @cfg {Boolean} keyboardNavigation default true
12475  * @cfg {String} language default en
12476  * 
12477  * @constructor
12478  * Create a new DateField
12479  * @param {Object} config The config object
12480  */
12481
12482 Roo.bootstrap.DateField = function(config){
12483     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
12484      this.addEvents({
12485             /**
12486              * @event show
12487              * Fires when this field show.
12488              * @param {Roo.bootstrap.DateField} this
12489              * @param {Mixed} date The date value
12490              */
12491             show : true,
12492             /**
12493              * @event show
12494              * Fires when this field hide.
12495              * @param {Roo.bootstrap.DateField} this
12496              * @param {Mixed} date The date value
12497              */
12498             hide : true,
12499             /**
12500              * @event select
12501              * Fires when select a date.
12502              * @param {Roo.bootstrap.DateField} this
12503              * @param {Mixed} date The date value
12504              */
12505             select : true
12506         });
12507 };
12508
12509 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
12510     
12511     /**
12512      * @cfg {String} format
12513      * The default date format string which can be overriden for localization support.  The format must be
12514      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
12515      */
12516     format : "m/d/y",
12517     /**
12518      * @cfg {String} altFormats
12519      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
12520      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
12521      */
12522     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
12523     
12524     weekStart : 0,
12525     
12526     viewMode : '',
12527     
12528     minViewMode : '',
12529     
12530     todayHighlight : false,
12531     
12532     todayBtn: false,
12533     
12534     language: 'en',
12535     
12536     keyboardNavigation: true,
12537     
12538     calendarWeeks: false,
12539     
12540     startDate: -Infinity,
12541     
12542     endDate: Infinity,
12543     
12544     daysOfWeekDisabled: [],
12545     
12546     _events: [],
12547     
12548     UTCDate: function()
12549     {
12550         return new Date(Date.UTC.apply(Date, arguments));
12551     },
12552     
12553     UTCToday: function()
12554     {
12555         var today = new Date();
12556         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
12557     },
12558     
12559     getDate: function() {
12560             var d = this.getUTCDate();
12561             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
12562     },
12563     
12564     getUTCDate: function() {
12565             return this.date;
12566     },
12567     
12568     setDate: function(d) {
12569             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
12570     },
12571     
12572     setUTCDate: function(d) {
12573             this.date = d;
12574             this.setValue(this.formatDate(this.date));
12575     },
12576         
12577     onRender: function(ct, position)
12578     {
12579         
12580         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
12581         
12582         this.language = this.language || 'en';
12583         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
12584         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
12585         
12586         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
12587         this.format = this.format || 'm/d/y';
12588         this.isInline = false;
12589         this.isInput = true;
12590         this.component = this.el.select('.add-on', true).first() || false;
12591         this.component = (this.component && this.component.length === 0) ? false : this.component;
12592         this.hasInput = this.component && this.inputEL().length;
12593         
12594         if (typeof(this.minViewMode === 'string')) {
12595             switch (this.minViewMode) {
12596                 case 'months':
12597                     this.minViewMode = 1;
12598                     break;
12599                 case 'years':
12600                     this.minViewMode = 2;
12601                     break;
12602                 default:
12603                     this.minViewMode = 0;
12604                     break;
12605             }
12606         }
12607         
12608         if (typeof(this.viewMode === 'string')) {
12609             switch (this.viewMode) {
12610                 case 'months':
12611                     this.viewMode = 1;
12612                     break;
12613                 case 'years':
12614                     this.viewMode = 2;
12615                     break;
12616                 default:
12617                     this.viewMode = 0;
12618                     break;
12619             }
12620         }
12621                 
12622         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
12623         
12624         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
12625         
12626         this.picker().on('mousedown', this.onMousedown, this);
12627         this.picker().on('click', this.onClick, this);
12628         
12629         this.picker().addClass('datepicker-dropdown');
12630         
12631         this.startViewMode = this.viewMode;
12632         
12633         
12634         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
12635             if(!this.calendarWeeks){
12636                 v.remove();
12637                 return;
12638             };
12639             
12640             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
12641             v.attr('colspan', function(i, val){
12642                 return parseInt(val) + 1;
12643             });
12644         })
12645                         
12646         
12647         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
12648         
12649         this.setStartDate(this.startDate);
12650         this.setEndDate(this.endDate);
12651         
12652         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
12653         
12654         this.fillDow();
12655         this.fillMonths();
12656         this.update();
12657         this.showMode();
12658         
12659         if(this.isInline) {
12660             this.show();
12661         }
12662     },
12663     
12664     picker : function()
12665     {
12666         return this.el.select('.datepicker', true).first();
12667     },
12668     
12669     fillDow: function()
12670     {
12671         var dowCnt = this.weekStart;
12672         
12673         var dow = {
12674             tag: 'tr',
12675             cn: [
12676                 
12677             ]
12678         };
12679         
12680         if(this.calendarWeeks){
12681             dow.cn.push({
12682                 tag: 'th',
12683                 cls: 'cw',
12684                 html: '&nbsp;'
12685             })
12686         }
12687         
12688         while (dowCnt < this.weekStart + 7) {
12689             dow.cn.push({
12690                 tag: 'th',
12691                 cls: 'dow',
12692                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
12693             });
12694         }
12695         
12696         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
12697     },
12698     
12699     fillMonths: function()
12700     {    
12701         var i = 0
12702         var months = this.picker().select('>.datepicker-months td', true).first();
12703         
12704         months.dom.innerHTML = '';
12705         
12706         while (i < 12) {
12707             var month = {
12708                 tag: 'span',
12709                 cls: 'month',
12710                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
12711             }
12712             
12713             months.createChild(month);
12714         }
12715         
12716     },
12717     
12718     update: function(){
12719         
12720         this.date = (typeof(this.date) === 'undefined') ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
12721         
12722         if (this.date < this.startDate) {
12723             this.viewDate = new Date(this.startDate);
12724         } else if (this.date > this.endDate) {
12725             this.viewDate = new Date(this.endDate);
12726         } else {
12727             this.viewDate = new Date(this.date);
12728         }
12729         
12730         this.fill();
12731     },
12732     
12733     fill: function() {
12734         var d = new Date(this.viewDate),
12735                 year = d.getUTCFullYear(),
12736                 month = d.getUTCMonth(),
12737                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
12738                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
12739                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
12740                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
12741                 currentDate = this.date && this.date.valueOf(),
12742                 today = this.UTCToday();
12743         
12744         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
12745         
12746 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
12747         
12748 //        this.picker.select('>tfoot th.today').
12749 //                                              .text(dates[this.language].today)
12750 //                                              .toggle(this.todayBtn !== false);
12751     
12752         this.updateNavArrows();
12753         this.fillMonths();
12754                                                 
12755         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
12756         
12757         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
12758          
12759         prevMonth.setUTCDate(day);
12760         
12761         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
12762         
12763         var nextMonth = new Date(prevMonth);
12764         
12765         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
12766         
12767         nextMonth = nextMonth.valueOf();
12768         
12769         var fillMonths = false;
12770         
12771         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
12772         
12773         while(prevMonth.valueOf() < nextMonth) {
12774             var clsName = '';
12775             
12776             if (prevMonth.getUTCDay() === this.weekStart) {
12777                 if(fillMonths){
12778                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
12779                 }
12780                     
12781                 fillMonths = {
12782                     tag: 'tr',
12783                     cn: []
12784                 };
12785                 
12786                 if(this.calendarWeeks){
12787                     // ISO 8601: First week contains first thursday.
12788                     // ISO also states week starts on Monday, but we can be more abstract here.
12789                     var
12790                     // Start of current week: based on weekstart/current date
12791                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
12792                     // Thursday of this week
12793                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
12794                     // First Thursday of year, year from thursday
12795                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
12796                     // Calendar week: ms between thursdays, div ms per day, div 7 days
12797                     calWeek =  (th - yth) / 864e5 / 7 + 1;
12798                     
12799                     fillMonths.cn.push({
12800                         tag: 'td',
12801                         cls: 'cw',
12802                         html: calWeek
12803                     });
12804                 }
12805             }
12806             
12807             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
12808                 clsName += ' old';
12809             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
12810                 clsName += ' new';
12811             }
12812             if (this.todayHighlight &&
12813                 prevMonth.getUTCFullYear() == today.getFullYear() &&
12814                 prevMonth.getUTCMonth() == today.getMonth() &&
12815                 prevMonth.getUTCDate() == today.getDate()) {
12816                 clsName += ' today';
12817             }
12818             
12819             if (currentDate && prevMonth.valueOf() === currentDate) {
12820                 clsName += ' active';
12821             }
12822             
12823             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
12824                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
12825                     clsName += ' disabled';
12826             }
12827             
12828             fillMonths.cn.push({
12829                 tag: 'td',
12830                 cls: 'day ' + clsName,
12831                 html: prevMonth.getDate()
12832             })
12833             
12834             prevMonth.setDate(prevMonth.getDate()+1);
12835         }
12836           
12837         var currentYear = this.date && this.date.getUTCFullYear();
12838         var currentMonth = this.date && this.date.getUTCMonth();
12839         
12840         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
12841         
12842         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
12843             v.removeClass('active');
12844             
12845             if(currentYear === year && k === currentMonth){
12846                 v.addClass('active');
12847             }
12848             
12849             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
12850                 v.addClass('disabled');
12851             }
12852             
12853         });
12854         
12855         
12856         year = parseInt(year/10, 10) * 10;
12857         
12858         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
12859         
12860         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
12861         
12862         year -= 1;
12863         for (var i = -1; i < 11; i++) {
12864             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
12865                 tag: 'span',
12866                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
12867                 html: year
12868             })
12869             
12870             year += 1;
12871         }
12872     },
12873     
12874     showMode: function(dir) {
12875         if (dir) {
12876             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
12877         }
12878         Roo.each(this.picker().select('>div',true).elements, function(v){
12879             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
12880             v.hide();
12881         });
12882         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
12883     },
12884     
12885     place: function()
12886     {
12887         if(this.isInline) return;
12888         
12889         this.picker().removeClass(['bottom', 'top']);
12890         
12891         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
12892             /*
12893              * place to the top of element!
12894              *
12895              */
12896             
12897             this.picker().addClass('top');
12898             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12899             
12900             return;
12901         }
12902         
12903         this.picker().addClass('bottom');
12904         
12905         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12906     },
12907     
12908     parseDate : function(value){
12909         if(!value || value instanceof Date){
12910             return value;
12911         }
12912         var v = Date.parseDate(value, this.format);
12913         if (!v && this.useIso) {
12914             v = Date.parseDate(value, 'Y-m-d');
12915         }
12916         if(!v && this.altFormats){
12917             if(!this.altFormatsArray){
12918                 this.altFormatsArray = this.altFormats.split("|");
12919             }
12920             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
12921                 v = Date.parseDate(value, this.altFormatsArray[i]);
12922             }
12923         }
12924         return v;
12925     },
12926     
12927     formatDate : function(date, fmt){
12928         return (!date || !(date instanceof Date)) ?
12929         date : date.dateFormat(fmt || this.format);
12930     },
12931     
12932     onFocus : function()
12933     {
12934         Roo.bootstrap.DateField.superclass.onFocus.call(this);
12935         this.show();
12936     },
12937     
12938     onBlur : function()
12939     {
12940         Roo.bootstrap.DateField.superclass.onBlur.call(this);
12941         this.hide();
12942     },
12943     
12944     show : function()
12945     {
12946         this.picker().show();
12947         this.update();
12948         this.place();
12949         
12950         this.fireEvent('show', this, this.date);
12951     },
12952     
12953     hide : function()
12954     {
12955         if(this.isInline) return;
12956         this.picker().hide();
12957         this.viewMode = this.startViewMode;
12958         this.showMode();
12959         
12960         this.fireEvent('hide', this, this.date);
12961         
12962     },
12963     
12964     onMousedown: function(e){
12965         e.stopPropagation();
12966         e.preventDefault();
12967     },
12968     
12969     keyup: function(e){
12970         Roo.bootstrap.DateField.superclass.keyup.call(this);
12971         this.update();
12972         
12973     },
12974
12975     setValue: function(v){
12976         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
12977         
12978         this.fireEvent('select', this, this.date);
12979         
12980     },
12981     
12982     fireKey: function(e){
12983         if (!this.picker().isVisible()){
12984             if (e.keyCode == 27) // allow escape to hide and re-show picker
12985                 this.show();
12986             return;
12987         }
12988         var dateChanged = false,
12989         dir, day, month,
12990         newDate, newViewDate;
12991         switch(e.keyCode){
12992             case 27: // escape
12993                 this.hide();
12994                 e.preventDefault();
12995                 break;
12996             case 37: // left
12997             case 39: // right
12998                 if (!this.keyboardNavigation) break;
12999                 dir = e.keyCode == 37 ? -1 : 1;
13000                 
13001                 if (e.ctrlKey){
13002                     newDate = this.moveYear(this.date, dir);
13003                     newViewDate = this.moveYear(this.viewDate, dir);
13004                 } else if (e.shiftKey){
13005                     newDate = this.moveMonth(this.date, dir);
13006                     newViewDate = this.moveMonth(this.viewDate, dir);
13007                 } else {
13008                     newDate = new Date(this.date);
13009                     newDate.setUTCDate(this.date.getUTCDate() + dir);
13010                     newViewDate = new Date(this.viewDate);
13011                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
13012                 }
13013                 if (this.dateWithinRange(newDate)){
13014                     this.date = newDate;
13015                     this.viewDate = newViewDate;
13016                     this.setValue(this.formatDate(this.date));
13017                     this.update();
13018                     e.preventDefault();
13019                     dateChanged = true;
13020                 }
13021                 break;
13022             case 38: // up
13023             case 40: // down
13024                 if (!this.keyboardNavigation) break;
13025                 dir = e.keyCode == 38 ? -1 : 1;
13026                 if (e.ctrlKey){
13027                     newDate = this.moveYear(this.date, dir);
13028                     newViewDate = this.moveYear(this.viewDate, dir);
13029                 } else if (e.shiftKey){
13030                     newDate = this.moveMonth(this.date, dir);
13031                     newViewDate = this.moveMonth(this.viewDate, dir);
13032                 } else {
13033                     newDate = new Date(this.date);
13034                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
13035                     newViewDate = new Date(this.viewDate);
13036                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
13037                 }
13038                 if (this.dateWithinRange(newDate)){
13039                     this.date = newDate;
13040                     this.viewDate = newViewDate;
13041                     this.setValue(this.formatDate(this.date));
13042                     this.update();
13043                     e.preventDefault();
13044                     dateChanged = true;
13045                 }
13046                 break;
13047             case 13: // enter
13048                 this.setValue(this.formatDate(this.date));
13049                 this.hide();
13050                 e.preventDefault();
13051                 break;
13052             case 9: // tab
13053                 this.setValue(this.formatDate(this.date));
13054                 this.hide();
13055                 break;
13056         }
13057     },
13058     
13059     
13060     onClick: function(e) {
13061         e.stopPropagation();
13062         e.preventDefault();
13063         
13064         var target = e.getTarget();
13065         
13066         if(target.nodeName.toLowerCase() === 'i'){
13067             target = Roo.get(target).dom.parentNode;
13068         }
13069         
13070         var nodeName = target.nodeName;
13071         var className = target.className;
13072         var html = target.innerHTML;
13073         
13074         switch(nodeName.toLowerCase()) {
13075             case 'th':
13076                 switch(className) {
13077                     case 'switch':
13078                         this.showMode(1);
13079                         break;
13080                     case 'prev':
13081                     case 'next':
13082                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
13083                         switch(this.viewMode){
13084                                 case 0:
13085                                         this.viewDate = this.moveMonth(this.viewDate, dir);
13086                                         break;
13087                                 case 1:
13088                                 case 2:
13089                                         this.viewDate = this.moveYear(this.viewDate, dir);
13090                                         break;
13091                         }
13092                         this.fill();
13093                         break;
13094                     case 'today':
13095                         var date = new Date();
13096                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
13097                         this.fill()
13098                         this.setValue(this.formatDate(this.date));
13099                         this.hide();
13100                         break;
13101                 }
13102                 break;
13103             case 'span':
13104                 if (className.indexOf('disabled') === -1) {
13105                     this.viewDate.setUTCDate(1);
13106                     if (className.indexOf('month') !== -1) {
13107                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
13108                     } else {
13109                         var year = parseInt(html, 10) || 0;
13110                         this.viewDate.setUTCFullYear(year);
13111                         
13112                     }
13113                     this.showMode(-1);
13114                     this.fill();
13115                 }
13116                 break;
13117                 
13118             case 'td':
13119                 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
13120                     var day = parseInt(html, 10) || 1;
13121                     var year = this.viewDate.getUTCFullYear(),
13122                         month = this.viewDate.getUTCMonth();
13123
13124                     if (className.indexOf('old') !== -1) {
13125                         if(month === 0 ){
13126                             month = 11;
13127                             year -= 1;
13128                         }else{
13129                             month -= 1;
13130                         }
13131                     } else if (className.indexOf('new') !== -1) {
13132                         if (month == 11) {
13133                             month = 0;
13134                             year += 1;
13135                         } else {
13136                             month += 1;
13137                         }
13138                     }
13139                     this.date = this.UTCDate(year, month, day,0,0,0,0);
13140                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
13141                     this.fill();
13142                     this.setValue(this.formatDate(this.date));
13143                     this.hide();
13144                 }
13145                 break;
13146         }
13147     },
13148     
13149     setStartDate: function(startDate){
13150         this.startDate = startDate || -Infinity;
13151         if (this.startDate !== -Infinity) {
13152             this.startDate = this.parseDate(this.startDate);
13153         }
13154         this.update();
13155         this.updateNavArrows();
13156     },
13157
13158     setEndDate: function(endDate){
13159         this.endDate = endDate || Infinity;
13160         if (this.endDate !== Infinity) {
13161             this.endDate = this.parseDate(this.endDate);
13162         }
13163         this.update();
13164         this.updateNavArrows();
13165     },
13166     
13167     setDaysOfWeekDisabled: function(daysOfWeekDisabled){
13168         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
13169         if (typeof(this.daysOfWeekDisabled) !== 'object') {
13170             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
13171         }
13172         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
13173             return parseInt(d, 10);
13174         });
13175         this.update();
13176         this.updateNavArrows();
13177     },
13178     
13179     updateNavArrows: function() {
13180         var d = new Date(this.viewDate),
13181         year = d.getUTCFullYear(),
13182         month = d.getUTCMonth();
13183         
13184         Roo.each(this.picker().select('.prev', true).elements, function(v){
13185             v.show();
13186             switch (this.viewMode) {
13187                 case 0:
13188
13189                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
13190                         v.hide();
13191                     }
13192                     break;
13193                 case 1:
13194                 case 2:
13195                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
13196                         v.hide();
13197                     }
13198                     break;
13199             }
13200         });
13201         
13202         Roo.each(this.picker().select('.next', true).elements, function(v){
13203             v.show();
13204             switch (this.viewMode) {
13205                 case 0:
13206
13207                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
13208                         v.hide();
13209                     }
13210                     break;
13211                 case 1:
13212                 case 2:
13213                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
13214                         v.hide();
13215                     }
13216                     break;
13217             }
13218         })
13219     },
13220     
13221     moveMonth: function(date, dir){
13222         if (!dir) return date;
13223         var new_date = new Date(date.valueOf()),
13224         day = new_date.getUTCDate(),
13225         month = new_date.getUTCMonth(),
13226         mag = Math.abs(dir),
13227         new_month, test;
13228         dir = dir > 0 ? 1 : -1;
13229         if (mag == 1){
13230             test = dir == -1
13231             // If going back one month, make sure month is not current month
13232             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
13233             ? function(){
13234                 return new_date.getUTCMonth() == month;
13235             }
13236             // If going forward one month, make sure month is as expected
13237             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
13238             : function(){
13239                 return new_date.getUTCMonth() != new_month;
13240             };
13241             new_month = month + dir;
13242             new_date.setUTCMonth(new_month);
13243             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
13244             if (new_month < 0 || new_month > 11)
13245                 new_month = (new_month + 12) % 12;
13246         } else {
13247             // For magnitudes >1, move one month at a time...
13248             for (var i=0; i<mag; i++)
13249                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
13250                 new_date = this.moveMonth(new_date, dir);
13251             // ...then reset the day, keeping it in the new month
13252             new_month = new_date.getUTCMonth();
13253             new_date.setUTCDate(day);
13254             test = function(){
13255                 return new_month != new_date.getUTCMonth();
13256             };
13257         }
13258         // Common date-resetting loop -- if date is beyond end of month, make it
13259         // end of month
13260         while (test()){
13261             new_date.setUTCDate(--day);
13262             new_date.setUTCMonth(new_month);
13263         }
13264         return new_date;
13265     },
13266
13267     moveYear: function(date, dir){
13268         return this.moveMonth(date, dir*12);
13269     },
13270
13271     dateWithinRange: function(date){
13272         return date >= this.startDate && date <= this.endDate;
13273     },
13274
13275     
13276     remove: function() {
13277         this.picker().remove();
13278     }
13279    
13280 });
13281
13282 Roo.apply(Roo.bootstrap.DateField,  {
13283     
13284     head : {
13285         tag: 'thead',
13286         cn: [
13287         {
13288             tag: 'tr',
13289             cn: [
13290             {
13291                 tag: 'th',
13292                 cls: 'prev',
13293                 html: '<i class="icon-arrow-left"/>'
13294             },
13295             {
13296                 tag: 'th',
13297                 cls: 'switch',
13298                 colspan: '5'
13299             },
13300             {
13301                 tag: 'th',
13302                 cls: 'next',
13303                 html: '<i class="icon-arrow-right"/>'
13304             }
13305
13306             ]
13307         }
13308         ]
13309     },
13310     
13311     content : {
13312         tag: 'tbody',
13313         cn: [
13314         {
13315             tag: 'tr',
13316             cn: [
13317             {
13318                 tag: 'td',
13319                 colspan: '7'
13320             }
13321             ]
13322         }
13323         ]
13324     },
13325     
13326     footer : {
13327         tag: 'tfoot',
13328         cn: [
13329         {
13330             tag: 'tr',
13331             cn: [
13332             {
13333                 tag: 'th',
13334                 colspan: '7',
13335                 cls: 'today'
13336             }
13337                     
13338             ]
13339         }
13340         ]
13341     },
13342     
13343     dates:{
13344         en: {
13345             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
13346             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
13347             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
13348             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
13349             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
13350             today: "Today"
13351         }
13352     },
13353     
13354     modes: [
13355     {
13356         clsName: 'days',
13357         navFnc: 'Month',
13358         navStep: 1
13359     },
13360     {
13361         clsName: 'months',
13362         navFnc: 'FullYear',
13363         navStep: 1
13364     },
13365     {
13366         clsName: 'years',
13367         navFnc: 'FullYear',
13368         navStep: 10
13369     }]
13370 });
13371
13372 Roo.apply(Roo.bootstrap.DateField,  {
13373   
13374     template : {
13375         tag: 'div',
13376         cls: 'datepicker dropdown-menu',
13377         cn: [
13378         {
13379             tag: 'div',
13380             cls: 'datepicker-days',
13381             cn: [
13382             {
13383                 tag: 'table',
13384                 cls: 'table-condensed',
13385                 cn:[
13386                 Roo.bootstrap.DateField.head,
13387                 {
13388                     tag: 'tbody'
13389                 },
13390                 Roo.bootstrap.DateField.footer
13391                 ]
13392             }
13393             ]
13394         },
13395         {
13396             tag: 'div',
13397             cls: 'datepicker-months',
13398             cn: [
13399             {
13400                 tag: 'table',
13401                 cls: 'table-condensed',
13402                 cn:[
13403                 Roo.bootstrap.DateField.head,
13404                 Roo.bootstrap.DateField.content,
13405                 Roo.bootstrap.DateField.footer
13406                 ]
13407             }
13408             ]
13409         },
13410         {
13411             tag: 'div',
13412             cls: 'datepicker-years',
13413             cn: [
13414             {
13415                 tag: 'table',
13416                 cls: 'table-condensed',
13417                 cn:[
13418                 Roo.bootstrap.DateField.head,
13419                 Roo.bootstrap.DateField.content,
13420                 Roo.bootstrap.DateField.footer
13421                 ]
13422             }
13423             ]
13424         }
13425         ]
13426     }
13427 });
13428
13429  
13430
13431  /*
13432  * - LGPL
13433  *
13434  * TimeField
13435  * 
13436  */
13437
13438 /**
13439  * @class Roo.bootstrap.TimeField
13440  * @extends Roo.bootstrap.Input
13441  * Bootstrap DateField class
13442  * 
13443  * 
13444  * @constructor
13445  * Create a new TimeField
13446  * @param {Object} config The config object
13447  */
13448
13449 Roo.bootstrap.TimeField = function(config){
13450     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
13451     this.addEvents({
13452             /**
13453              * @event show
13454              * Fires when this field show.
13455              * @param {Roo.bootstrap.DateField} this
13456              * @param {Mixed} date The date value
13457              */
13458             show : true,
13459             /**
13460              * @event show
13461              * Fires when this field hide.
13462              * @param {Roo.bootstrap.DateField} this
13463              * @param {Mixed} date The date value
13464              */
13465             hide : true,
13466             /**
13467              * @event select
13468              * Fires when select a date.
13469              * @param {Roo.bootstrap.DateField} this
13470              * @param {Mixed} date The date value
13471              */
13472             select : true
13473         });
13474 };
13475
13476 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
13477     
13478     /**
13479      * @cfg {String} format
13480      * The default time format string which can be overriden for localization support.  The format must be
13481      * valid according to {@link Date#parseDate} (defaults to 'H:i').
13482      */
13483     format : "H:i",
13484        
13485     onRender: function(ct, position)
13486     {
13487         
13488         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
13489                 
13490         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
13491         
13492         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13493         
13494         this.pop = this.picker().select('>.datepicker-time',true).first();
13495         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block' 
13496         
13497         this.picker().on('mousedown', this.onMousedown, this);
13498         this.picker().on('click', this.onClick, this);
13499         
13500         this.picker().addClass('datepicker-dropdown');
13501     
13502         this.fillTime();
13503         this.update();
13504             
13505         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
13506         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
13507         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
13508         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
13509         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
13510         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
13511
13512     },
13513     
13514     fireKey: function(e){
13515         if (!this.picker().isVisible()){
13516             if (e.keyCode == 27) // allow escape to hide and re-show picker
13517                 this.show();
13518             return;
13519         }
13520
13521         e.preventDefault();
13522         
13523         switch(e.keyCode){
13524             case 27: // escape
13525                 this.hide();
13526                 break;
13527             case 37: // left
13528             case 39: // right
13529                 this.onTogglePeriod();
13530                 break;
13531             case 38: // up
13532                 this.onIncrementMinutes();
13533                 break;
13534             case 40: // down
13535                 this.onDecrementMinutes();
13536                 break;
13537             case 13: // enter
13538             case 9: // tab
13539                 this.setTime();
13540                 break;
13541         }
13542     },
13543     
13544     onClick: function(e) {
13545         e.stopPropagation();
13546         e.preventDefault();
13547     },
13548     
13549     picker : function()
13550     {
13551         return this.el.select('.datepicker', true).first();
13552     },
13553     
13554     fillTime: function()
13555     {    
13556         var time = this.pop.select('tbody', true).first();
13557         
13558         time.dom.innerHTML = '';
13559         
13560         time.createChild({
13561             tag: 'tr',
13562             cn: [
13563                 {
13564                     tag: 'td',
13565                     cn: [
13566                         {
13567                             tag: 'a',
13568                             href: '#',
13569                             cls: 'btn',
13570                             cn: [
13571                                 {
13572                                     tag: 'span',
13573                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
13574                                 }
13575                             ]
13576                         } 
13577                     ]
13578                 },
13579                 {
13580                     tag: 'td',
13581                     cls: 'separator'
13582                 },
13583                 {
13584                     tag: 'td',
13585                     cn: [
13586                         {
13587                             tag: 'a',
13588                             href: '#',
13589                             cls: 'btn',
13590                             cn: [
13591                                 {
13592                                     tag: 'span',
13593                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
13594                                 }
13595                             ]
13596                         }
13597                     ]
13598                 },
13599                 {
13600                     tag: 'td',
13601                     cls: 'separator'
13602                 }
13603             ]
13604         });
13605         
13606         time.createChild({
13607             tag: 'tr',
13608             cn: [
13609                 {
13610                     tag: 'td',
13611                     cn: [
13612                         {
13613                             tag: 'span',
13614                             cls: 'timepicker-hour',
13615                             html: '00'
13616                         }  
13617                     ]
13618                 },
13619                 {
13620                     tag: 'td',
13621                     cls: 'separator',
13622                     html: ':'
13623                 },
13624                 {
13625                     tag: 'td',
13626                     cn: [
13627                         {
13628                             tag: 'span',
13629                             cls: 'timepicker-minute',
13630                             html: '00'
13631                         }  
13632                     ]
13633                 },
13634                 {
13635                     tag: 'td',
13636                     cls: 'separator'
13637                 },
13638                 {
13639                     tag: 'td',
13640                     cn: [
13641                         {
13642                             tag: 'button',
13643                             type: 'button',
13644                             cls: 'btn btn-primary period',
13645                             html: 'AM'
13646                             
13647                         }
13648                     ]
13649                 }
13650             ]
13651         });
13652         
13653         time.createChild({
13654             tag: 'tr',
13655             cn: [
13656                 {
13657                     tag: 'td',
13658                     cn: [
13659                         {
13660                             tag: 'a',
13661                             href: '#',
13662                             cls: 'btn',
13663                             cn: [
13664                                 {
13665                                     tag: 'span',
13666                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
13667                                 }
13668                             ]
13669                         }
13670                     ]
13671                 },
13672                 {
13673                     tag: 'td',
13674                     cls: 'separator'
13675                 },
13676                 {
13677                     tag: 'td',
13678                     cn: [
13679                         {
13680                             tag: 'a',
13681                             href: '#',
13682                             cls: 'btn',
13683                             cn: [
13684                                 {
13685                                     tag: 'span',
13686                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
13687                                 }
13688                             ]
13689                         }
13690                     ]
13691                 },
13692                 {
13693                     tag: 'td',
13694                     cls: 'separator'
13695                 }
13696             ]
13697         });
13698         
13699     },
13700     
13701     update: function()
13702     {
13703         
13704         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
13705         
13706         this.fill();
13707     },
13708     
13709     fill: function() 
13710     {
13711         var hours = this.time.getHours();
13712         var minutes = this.time.getMinutes();
13713         var period = 'AM';
13714         
13715         if(hours > 11){
13716             period = 'PM';
13717         }
13718         
13719         if(hours == 0){
13720             hours = 12;
13721         }
13722         
13723         
13724         if(hours > 12){
13725             hours = hours - 12;
13726         }
13727         
13728         if(hours < 10){
13729             hours = '0' + hours;
13730         }
13731         
13732         if(minutes < 10){
13733             minutes = '0' + minutes;
13734         }
13735         
13736         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
13737         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
13738         this.pop.select('button', true).first().dom.innerHTML = period;
13739         
13740     },
13741     
13742     place: function()
13743     {   
13744         this.picker().removeClass(['bottom', 'top']);
13745         
13746         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
13747             /*
13748              * place to the top of element!
13749              *
13750              */
13751             
13752             this.picker().addClass('top');
13753             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
13754             
13755             return;
13756         }
13757         
13758         this.picker().addClass('bottom');
13759         
13760         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
13761     },
13762   
13763     onFocus : function()
13764     {
13765         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
13766         this.show();
13767     },
13768     
13769     onBlur : function()
13770     {
13771         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
13772         this.hide();
13773     },
13774     
13775     show : function()
13776     {
13777         this.picker().show();
13778         this.pop.show();
13779         this.update();
13780         this.place();
13781         
13782         this.fireEvent('show', this, this.date);
13783     },
13784     
13785     hide : function()
13786     {
13787         this.picker().hide();
13788         this.pop.hide();
13789         
13790         this.fireEvent('hide', this, this.date);
13791     },
13792     
13793     setTime : function()
13794     {
13795         this.hide();
13796         this.setValue(this.time.format(this.format));
13797         
13798         this.fireEvent('select', this, this.date);
13799         
13800         
13801     },
13802     
13803     onMousedown: function(e){
13804         e.stopPropagation();
13805         e.preventDefault();
13806     },
13807     
13808     onIncrementHours: function()
13809     {
13810         Roo.log('onIncrementHours');
13811         this.time = this.time.add(Date.HOUR, 1);
13812         this.update();
13813         
13814     },
13815     
13816     onDecrementHours: function()
13817     {
13818         Roo.log('onDecrementHours');
13819         this.time = this.time.add(Date.HOUR, -1);
13820         this.update();
13821     },
13822     
13823     onIncrementMinutes: function()
13824     {
13825         Roo.log('onIncrementMinutes');
13826         this.time = this.time.add(Date.MINUTE, 1);
13827         this.update();
13828     },
13829     
13830     onDecrementMinutes: function()
13831     {
13832         Roo.log('onDecrementMinutes');
13833         this.time = this.time.add(Date.MINUTE, -1);
13834         this.update();
13835     },
13836     
13837     onTogglePeriod: function()
13838     {
13839         Roo.log('onTogglePeriod');
13840         this.time = this.time.add(Date.HOUR, 12);
13841         this.update();
13842     }
13843     
13844    
13845 });
13846
13847 Roo.apply(Roo.bootstrap.TimeField,  {
13848     
13849     content : {
13850         tag: 'tbody',
13851         cn: [
13852             {
13853                 tag: 'tr',
13854                 cn: [
13855                 {
13856                     tag: 'td',
13857                     colspan: '7'
13858                 }
13859                 ]
13860             }
13861         ]
13862     },
13863     
13864     footer : {
13865         tag: 'tfoot',
13866         cn: [
13867             {
13868                 tag: 'tr',
13869                 cn: [
13870                 {
13871                     tag: 'th',
13872                     colspan: '7',
13873                     cls: '',
13874                     cn: [
13875                         {
13876                             tag: 'button',
13877                             cls: 'btn btn-info ok',
13878                             html: 'OK'
13879                         }
13880                     ]
13881                 }
13882
13883                 ]
13884             }
13885         ]
13886     }
13887 });
13888
13889 Roo.apply(Roo.bootstrap.TimeField,  {
13890   
13891     template : {
13892         tag: 'div',
13893         cls: 'datepicker dropdown-menu',
13894         cn: [
13895             {
13896                 tag: 'div',
13897                 cls: 'datepicker-time',
13898                 cn: [
13899                 {
13900                     tag: 'table',
13901                     cls: 'table-condensed',
13902                     cn:[
13903                     Roo.bootstrap.TimeField.content,
13904                     Roo.bootstrap.TimeField.footer
13905                     ]
13906                 }
13907                 ]
13908             }
13909         ]
13910     }
13911 });
13912
13913  
13914
13915  /*
13916  * - LGPL
13917  *
13918  * CheckBox
13919  * 
13920  */
13921
13922 /**
13923  * @class Roo.bootstrap.CheckBox
13924  * @extends Roo.bootstrap.Input
13925  * Bootstrap CheckBox class
13926  * 
13927  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
13928  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
13929  * @cfg {String} boxLabel The text that appears beside the checkbox
13930  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
13931  * @cfg {Boolean} checked initnal the element
13932  * 
13933  * 
13934  * @constructor
13935  * Create a new CheckBox
13936  * @param {Object} config The config object
13937  */
13938
13939 Roo.bootstrap.CheckBox = function(config){
13940     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
13941    
13942         this.addEvents({
13943             /**
13944             * @event check
13945             * Fires when the element is checked or unchecked.
13946             * @param {Roo.bootstrap.CheckBox} this This input
13947             * @param {Boolean} checked The new checked value
13948             */
13949            check : true
13950         });
13951 };
13952
13953 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
13954     
13955     inputType: 'checkbox',
13956     inputValue: 1,
13957     valueOff: 0,
13958     boxLabel: false,
13959     checked: false,
13960     weight : false,
13961     
13962     getAutoCreate : function()
13963     {
13964         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
13965         
13966         var id = Roo.id();
13967         
13968         var cfg = {};
13969         
13970         cfg.cls = 'form-group checkbox' //input-group
13971         
13972         
13973         
13974         
13975         var input =  {
13976             tag: 'input',
13977             id : id,
13978             type : this.inputType,
13979             value : (!this.checked) ? this.valueOff : this.inputValue,
13980             cls : 'roo-checkbox', //'form-box',
13981             placeholder : this.placeholder || ''
13982             
13983         };
13984         
13985         if (this.weight) { // Validity check?
13986             cfg.cls += " checkbox-" + this.weight;
13987         }
13988         
13989         if (this.disabled) {
13990             input.disabled=true;
13991         }
13992         
13993         if(this.checked){
13994             input.checked = this.checked;
13995         }
13996         
13997         if (this.name) {
13998             input.name = this.name;
13999         }
14000         
14001         if (this.size) {
14002             input.cls += ' input-' + this.size;
14003         }
14004         
14005         var settings=this;
14006         ['xs','sm','md','lg'].map(function(size){
14007             if (settings[size]) {
14008                 cfg.cls += ' col-' + size + '-' + settings[size];
14009             }
14010         });
14011         
14012        
14013         
14014         var inputblock = input;
14015         
14016         
14017         
14018         
14019         if (this.before || this.after) {
14020             
14021             inputblock = {
14022                 cls : 'input-group',
14023                 cn :  [] 
14024             };
14025             if (this.before) {
14026                 inputblock.cn.push({
14027                     tag :'span',
14028                     cls : 'input-group-addon',
14029                     html : this.before
14030                 });
14031             }
14032             inputblock.cn.push(input);
14033             if (this.after) {
14034                 inputblock.cn.push({
14035                     tag :'span',
14036                     cls : 'input-group-addon',
14037                     html : this.after
14038                 });
14039             }
14040             
14041         };
14042         
14043         if (align ==='left' && this.fieldLabel.length) {
14044                 Roo.log("left and has label");
14045                 cfg.cn = [
14046                     
14047                     {
14048                         tag: 'label',
14049                         'for' :  id,
14050                         cls : 'control-label col-md-' + this.labelWidth,
14051                         html : this.fieldLabel
14052                         
14053                     },
14054                     {
14055                         cls : "col-md-" + (12 - this.labelWidth), 
14056                         cn: [
14057                             inputblock
14058                         ]
14059                     }
14060                     
14061                 ];
14062         } else if ( this.fieldLabel.length) {
14063                 Roo.log(" label");
14064                 cfg.cn = [
14065                    
14066                     {
14067                         tag: this.boxLabel ? 'span' : 'label',
14068                         'for': id,
14069                         cls: 'control-label box-input-label',
14070                         //cls : 'input-group-addon',
14071                         html : this.fieldLabel
14072                         
14073                     },
14074                     
14075                     inputblock
14076                     
14077                 ];
14078
14079         } else {
14080             
14081                 Roo.log(" no label && no align");
14082                 cfg.cn = [  inputblock ] ;
14083                 
14084                 
14085         };
14086          if(this.boxLabel){
14087             cfg.cn.push( {
14088                 tag: 'label',
14089                 'for': id,
14090                 cls: 'box-label',
14091                 html: this.boxLabel
14092                 
14093             });
14094         }
14095         
14096         
14097        
14098         return cfg;
14099         
14100     },
14101     
14102     /**
14103      * return the real input element.
14104      */
14105     inputEl: function ()
14106     {
14107         return this.el.select('input.roo-checkbox',true).first();
14108     },
14109     
14110     label: function()
14111     {
14112         return this.el.select('label.control-label',true).first();
14113     },
14114     
14115     initEvents : function()
14116     {
14117 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
14118         
14119         this.inputEl().on('click', this.onClick,  this);
14120         
14121     },
14122     
14123     onClick : function()
14124     {   
14125         this.setChecked(!this.checked);
14126     },
14127     
14128     setChecked : function(state,suppressEvent)
14129     {
14130         this.checked = state;
14131         
14132         this.inputEl().dom.checked = state;
14133         
14134         if(suppressEvent !== true){
14135             this.fireEvent('check', this, state);
14136         }
14137         
14138         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
14139         
14140     },
14141     
14142     setValue : function(v,suppressEvent)
14143     {
14144         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
14145     }
14146     
14147 });
14148
14149  
14150 /*
14151  * - LGPL
14152  *
14153  * Radio
14154  * 
14155  */
14156
14157 /**
14158  * @class Roo.bootstrap.Radio
14159  * @extends Roo.bootstrap.CheckBox
14160  * Bootstrap Radio class
14161
14162  * @constructor
14163  * Create a new Radio
14164  * @param {Object} config The config object
14165  */
14166
14167 Roo.bootstrap.Radio = function(config){
14168     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
14169    
14170 };
14171
14172 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
14173     
14174     inputType: 'radio',
14175     inputValue: '',
14176     valueOff: '',
14177     
14178     getAutoCreate : function()
14179     {
14180         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
14181         
14182         var id = Roo.id();
14183         
14184         var cfg = {};
14185         
14186         cfg.cls = 'form-group radio' //input-group
14187         
14188         var input =  {
14189             tag: 'input',
14190             id : id,
14191             type : this.inputType,
14192             value : (!this.checked) ? this.valueOff : this.inputValue,
14193             cls : 'roo-radio',
14194             placeholder : this.placeholder || ''
14195             
14196         };
14197           if (this.weight) { // Validity check?
14198             cfg.cls += " radio-" + this.weight;
14199         }
14200         if (this.disabled) {
14201             input.disabled=true;
14202         }
14203         
14204         if(this.checked){
14205             input.checked = this.checked;
14206         }
14207         
14208         if (this.name) {
14209             input.name = this.name;
14210         }
14211         
14212         if (this.size) {
14213             input.cls += ' input-' + this.size;
14214         }
14215         
14216         var settings=this;
14217         ['xs','sm','md','lg'].map(function(size){
14218             if (settings[size]) {
14219                 cfg.cls += ' col-' + size + '-' + settings[size];
14220             }
14221         });
14222         
14223         var inputblock = input;
14224         
14225         if (this.before || this.after) {
14226             
14227             inputblock = {
14228                 cls : 'input-group',
14229                 cn :  [] 
14230             };
14231             if (this.before) {
14232                 inputblock.cn.push({
14233                     tag :'span',
14234                     cls : 'input-group-addon',
14235                     html : this.before
14236                 });
14237             }
14238             inputblock.cn.push(input);
14239             if (this.after) {
14240                 inputblock.cn.push({
14241                     tag :'span',
14242                     cls : 'input-group-addon',
14243                     html : this.after
14244                 });
14245             }
14246             
14247         };
14248         
14249         if (align ==='left' && this.fieldLabel.length) {
14250                 Roo.log("left and has label");
14251                 cfg.cn = [
14252                     
14253                     {
14254                         tag: 'label',
14255                         'for' :  id,
14256                         cls : 'control-label col-md-' + this.labelWidth,
14257                         html : this.fieldLabel
14258                         
14259                     },
14260                     {
14261                         cls : "col-md-" + (12 - this.labelWidth), 
14262                         cn: [
14263                             inputblock
14264                         ]
14265                     }
14266                     
14267                 ];
14268         } else if ( this.fieldLabel.length) {
14269                 Roo.log(" label");
14270                  cfg.cn = [
14271                    
14272                     {
14273                         tag: 'label',
14274                         'for': id,
14275                         cls: 'control-label box-input-label',
14276                         //cls : 'input-group-addon',
14277                         html : this.fieldLabel
14278                         
14279                     },
14280                     
14281                     inputblock
14282                     
14283                 ];
14284
14285         } else {
14286             
14287                    Roo.log(" no label && no align");
14288                 cfg.cn = [
14289                     
14290                         inputblock
14291                     
14292                 ];
14293                 
14294                 
14295         };
14296         
14297         if(this.boxLabel){
14298             cfg.cn.push({
14299                 tag: 'label',
14300                 'for': id,
14301                 cls: 'box-label',
14302                 html: this.boxLabel
14303             })
14304         }
14305         
14306         return cfg;
14307         
14308     },
14309     inputEl: function ()
14310     {
14311         return this.el.select('input.roo-radio',true).first();
14312     },
14313     onClick : function()
14314     {   
14315         this.setChecked(true);
14316     },
14317     
14318     setChecked : function(state,suppressEvent)
14319     {
14320         if(state){
14321             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
14322                 v.dom.checked = false;
14323             });
14324         }
14325         
14326         this.checked = state;
14327         this.inputEl().dom.checked = state;
14328         
14329         if(suppressEvent !== true){
14330             this.fireEvent('check', this, state);
14331         }
14332         
14333         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
14334         
14335     },
14336     
14337     getGroupValue : function()
14338     {
14339         var value = ''
14340         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
14341             if(v.dom.checked == true){
14342                 value = v.dom.value;
14343             }
14344         });
14345         
14346         return value;
14347     },
14348     
14349     /**
14350      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
14351      * @return {Mixed} value The field value
14352      */
14353     getValue : function(){
14354         return this.getGroupValue();
14355     }
14356     
14357 });
14358
14359  
14360 //<script type="text/javascript">
14361
14362 /*
14363  * Based  Ext JS Library 1.1.1
14364  * Copyright(c) 2006-2007, Ext JS, LLC.
14365  * LGPL
14366  *
14367  */
14368  
14369 /**
14370  * @class Roo.HtmlEditorCore
14371  * @extends Roo.Component
14372  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
14373  *
14374  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
14375  */
14376
14377 Roo.HtmlEditorCore = function(config){
14378     
14379     
14380     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
14381     this.addEvents({
14382         /**
14383          * @event initialize
14384          * Fires when the editor is fully initialized (including the iframe)
14385          * @param {Roo.HtmlEditorCore} this
14386          */
14387         initialize: true,
14388         /**
14389          * @event activate
14390          * Fires when the editor is first receives the focus. Any insertion must wait
14391          * until after this event.
14392          * @param {Roo.HtmlEditorCore} this
14393          */
14394         activate: true,
14395          /**
14396          * @event beforesync
14397          * Fires before the textarea is updated with content from the editor iframe. Return false
14398          * to cancel the sync.
14399          * @param {Roo.HtmlEditorCore} this
14400          * @param {String} html
14401          */
14402         beforesync: true,
14403          /**
14404          * @event beforepush
14405          * Fires before the iframe editor is updated with content from the textarea. Return false
14406          * to cancel the push.
14407          * @param {Roo.HtmlEditorCore} this
14408          * @param {String} html
14409          */
14410         beforepush: true,
14411          /**
14412          * @event sync
14413          * Fires when the textarea is updated with content from the editor iframe.
14414          * @param {Roo.HtmlEditorCore} this
14415          * @param {String} html
14416          */
14417         sync: true,
14418          /**
14419          * @event push
14420          * Fires when the iframe editor is updated with content from the textarea.
14421          * @param {Roo.HtmlEditorCore} this
14422          * @param {String} html
14423          */
14424         push: true,
14425         
14426         /**
14427          * @event editorevent
14428          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
14429          * @param {Roo.HtmlEditorCore} this
14430          */
14431         editorevent: true
14432     });
14433      
14434 };
14435
14436
14437 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
14438
14439
14440      /**
14441      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
14442      */
14443     
14444     owner : false,
14445     
14446      /**
14447      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
14448      *                        Roo.resizable.
14449      */
14450     resizable : false,
14451      /**
14452      * @cfg {Number} height (in pixels)
14453      */   
14454     height: 300,
14455    /**
14456      * @cfg {Number} width (in pixels)
14457      */   
14458     width: 500,
14459     
14460     /**
14461      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
14462      * 
14463      */
14464     stylesheets: false,
14465     
14466     // id of frame..
14467     frameId: false,
14468     
14469     // private properties
14470     validationEvent : false,
14471     deferHeight: true,
14472     initialized : false,
14473     activated : false,
14474     sourceEditMode : false,
14475     onFocus : Roo.emptyFn,
14476     iframePad:3,
14477     hideMode:'offsets',
14478     
14479     clearUp: true,
14480     
14481      
14482     
14483
14484     /**
14485      * Protected method that will not generally be called directly. It
14486      * is called when the editor initializes the iframe with HTML contents. Override this method if you
14487      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
14488      */
14489     getDocMarkup : function(){
14490         // body styles..
14491         var st = '';
14492         Roo.log(this.stylesheets);
14493         
14494         // inherit styels from page...?? 
14495         if (this.stylesheets === false) {
14496             
14497             Roo.get(document.head).select('style').each(function(node) {
14498                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
14499             });
14500             
14501             Roo.get(document.head).select('link').each(function(node) { 
14502                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
14503             });
14504             
14505         } else if (!this.stylesheets.length) {
14506                 // simple..
14507                 st = '<style type="text/css">' +
14508                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
14509                    '</style>';
14510         } else {
14511             Roo.each(this.stylesheets, function(s) {
14512                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
14513             });
14514             
14515         }
14516         
14517         st +=  '<style type="text/css">' +
14518             'IMG { cursor: pointer } ' +
14519         '</style>';
14520
14521         
14522         return '<html><head>' + st  +
14523             //<style type="text/css">' +
14524             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
14525             //'</style>' +
14526             ' </head><body class="roo-htmleditor-body"></body></html>';
14527     },
14528
14529     // private
14530     onRender : function(ct, position)
14531     {
14532         var _t = this;
14533         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
14534         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
14535         
14536         
14537         this.el.dom.style.border = '0 none';
14538         this.el.dom.setAttribute('tabIndex', -1);
14539         this.el.addClass('x-hidden hide');
14540         
14541         
14542         
14543         if(Roo.isIE){ // fix IE 1px bogus margin
14544             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
14545         }
14546        
14547         
14548         this.frameId = Roo.id();
14549         
14550          
14551         
14552         var iframe = this.owner.wrap.createChild({
14553             tag: 'iframe',
14554             cls: 'form-control', // bootstrap..
14555             id: this.frameId,
14556             name: this.frameId,
14557             frameBorder : 'no',
14558             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
14559         }, this.el
14560         );
14561         
14562         
14563         this.iframe = iframe.dom;
14564
14565          this.assignDocWin();
14566         
14567         this.doc.designMode = 'on';
14568        
14569         this.doc.open();
14570         this.doc.write(this.getDocMarkup());
14571         this.doc.close();
14572
14573         
14574         var task = { // must defer to wait for browser to be ready
14575             run : function(){
14576                 //console.log("run task?" + this.doc.readyState);
14577                 this.assignDocWin();
14578                 if(this.doc.body || this.doc.readyState == 'complete'){
14579                     try {
14580                         this.doc.designMode="on";
14581                     } catch (e) {
14582                         return;
14583                     }
14584                     Roo.TaskMgr.stop(task);
14585                     this.initEditor.defer(10, this);
14586                 }
14587             },
14588             interval : 10,
14589             duration: 10000,
14590             scope: this
14591         };
14592         Roo.TaskMgr.start(task);
14593
14594         
14595          
14596     },
14597
14598     // private
14599     onResize : function(w, h)
14600     {
14601          Roo.log('resize: ' +w + ',' + h );
14602         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
14603         if(!this.iframe){
14604             return;
14605         }
14606         if(typeof w == 'number'){
14607             
14608             this.iframe.style.width = w + 'px';
14609         }
14610         if(typeof h == 'number'){
14611             
14612             this.iframe.style.height = h + 'px';
14613             if(this.doc){
14614                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
14615             }
14616         }
14617         
14618     },
14619
14620     /**
14621      * Toggles the editor between standard and source edit mode.
14622      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
14623      */
14624     toggleSourceEdit : function(sourceEditMode){
14625         
14626         this.sourceEditMode = sourceEditMode === true;
14627         
14628         if(this.sourceEditMode){
14629  
14630             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
14631             
14632         }else{
14633             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
14634             //this.iframe.className = '';
14635             this.deferFocus();
14636         }
14637         //this.setSize(this.owner.wrap.getSize());
14638         //this.fireEvent('editmodechange', this, this.sourceEditMode);
14639     },
14640
14641     
14642   
14643
14644     /**
14645      * Protected method that will not generally be called directly. If you need/want
14646      * custom HTML cleanup, this is the method you should override.
14647      * @param {String} html The HTML to be cleaned
14648      * return {String} The cleaned HTML
14649      */
14650     cleanHtml : function(html){
14651         html = String(html);
14652         if(html.length > 5){
14653             if(Roo.isSafari){ // strip safari nonsense
14654                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
14655             }
14656         }
14657         if(html == '&nbsp;'){
14658             html = '';
14659         }
14660         return html;
14661     },
14662
14663     /**
14664      * HTML Editor -> Textarea
14665      * Protected method that will not generally be called directly. Syncs the contents
14666      * of the editor iframe with the textarea.
14667      */
14668     syncValue : function(){
14669         if(this.initialized){
14670             var bd = (this.doc.body || this.doc.documentElement);
14671             //this.cleanUpPaste(); -- this is done else where and causes havoc..
14672             var html = bd.innerHTML;
14673             if(Roo.isSafari){
14674                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
14675                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
14676                 if(m && m[1]){
14677                     html = '<div style="'+m[0]+'">' + html + '</div>';
14678                 }
14679             }
14680             html = this.cleanHtml(html);
14681             // fix up the special chars.. normaly like back quotes in word...
14682             // however we do not want to do this with chinese..
14683             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
14684                 var cc = b.charCodeAt();
14685                 if (
14686                     (cc >= 0x4E00 && cc < 0xA000 ) ||
14687                     (cc >= 0x3400 && cc < 0x4E00 ) ||
14688                     (cc >= 0xf900 && cc < 0xfb00 )
14689                 ) {
14690                         return b;
14691                 }
14692                 return "&#"+cc+";" 
14693             });
14694             if(this.owner.fireEvent('beforesync', this, html) !== false){
14695                 this.el.dom.value = html;
14696                 this.owner.fireEvent('sync', this, html);
14697             }
14698         }
14699     },
14700
14701     /**
14702      * Protected method that will not generally be called directly. Pushes the value of the textarea
14703      * into the iframe editor.
14704      */
14705     pushValue : function(){
14706         if(this.initialized){
14707             var v = this.el.dom.value.trim();
14708             
14709 //            if(v.length < 1){
14710 //                v = '&#160;';
14711 //            }
14712             
14713             if(this.owner.fireEvent('beforepush', this, v) !== false){
14714                 var d = (this.doc.body || this.doc.documentElement);
14715                 d.innerHTML = v;
14716                 this.cleanUpPaste();
14717                 this.el.dom.value = d.innerHTML;
14718                 this.owner.fireEvent('push', this, v);
14719             }
14720         }
14721     },
14722
14723     // private
14724     deferFocus : function(){
14725         this.focus.defer(10, this);
14726     },
14727
14728     // doc'ed in Field
14729     focus : function(){
14730         if(this.win && !this.sourceEditMode){
14731             this.win.focus();
14732         }else{
14733             this.el.focus();
14734         }
14735     },
14736     
14737     assignDocWin: function()
14738     {
14739         var iframe = this.iframe;
14740         
14741          if(Roo.isIE){
14742             this.doc = iframe.contentWindow.document;
14743             this.win = iframe.contentWindow;
14744         } else {
14745             if (!Roo.get(this.frameId)) {
14746                 return;
14747             }
14748             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
14749             this.win = Roo.get(this.frameId).dom.contentWindow;
14750         }
14751     },
14752     
14753     // private
14754     initEditor : function(){
14755         //console.log("INIT EDITOR");
14756         this.assignDocWin();
14757         
14758         
14759         
14760         this.doc.designMode="on";
14761         this.doc.open();
14762         this.doc.write(this.getDocMarkup());
14763         this.doc.close();
14764         
14765         var dbody = (this.doc.body || this.doc.documentElement);
14766         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
14767         // this copies styles from the containing element into thsi one..
14768         // not sure why we need all of this..
14769         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
14770         ss['background-attachment'] = 'fixed'; // w3c
14771         dbody.bgProperties = 'fixed'; // ie
14772         Roo.DomHelper.applyStyles(dbody, ss);
14773         Roo.EventManager.on(this.doc, {
14774             //'mousedown': this.onEditorEvent,
14775             'mouseup': this.onEditorEvent,
14776             'dblclick': this.onEditorEvent,
14777             'click': this.onEditorEvent,
14778             'keyup': this.onEditorEvent,
14779             buffer:100,
14780             scope: this
14781         });
14782         if(Roo.isGecko){
14783             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
14784         }
14785         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
14786             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
14787         }
14788         this.initialized = true;
14789
14790         this.owner.fireEvent('initialize', this);
14791         this.pushValue();
14792     },
14793
14794     // private
14795     onDestroy : function(){
14796         
14797         
14798         
14799         if(this.rendered){
14800             
14801             //for (var i =0; i < this.toolbars.length;i++) {
14802             //    // fixme - ask toolbars for heights?
14803             //    this.toolbars[i].onDestroy();
14804            // }
14805             
14806             //this.wrap.dom.innerHTML = '';
14807             //this.wrap.remove();
14808         }
14809     },
14810
14811     // private
14812     onFirstFocus : function(){
14813         
14814         this.assignDocWin();
14815         
14816         
14817         this.activated = true;
14818          
14819     
14820         if(Roo.isGecko){ // prevent silly gecko errors
14821             this.win.focus();
14822             var s = this.win.getSelection();
14823             if(!s.focusNode || s.focusNode.nodeType != 3){
14824                 var r = s.getRangeAt(0);
14825                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
14826                 r.collapse(true);
14827                 this.deferFocus();
14828             }
14829             try{
14830                 this.execCmd('useCSS', true);
14831                 this.execCmd('styleWithCSS', false);
14832             }catch(e){}
14833         }
14834         this.owner.fireEvent('activate', this);
14835     },
14836
14837     // private
14838     adjustFont: function(btn){
14839         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
14840         //if(Roo.isSafari){ // safari
14841         //    adjust *= 2;
14842        // }
14843         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
14844         if(Roo.isSafari){ // safari
14845             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
14846             v =  (v < 10) ? 10 : v;
14847             v =  (v > 48) ? 48 : v;
14848             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
14849             
14850         }
14851         
14852         
14853         v = Math.max(1, v+adjust);
14854         
14855         this.execCmd('FontSize', v  );
14856     },
14857
14858     onEditorEvent : function(e){
14859         this.owner.fireEvent('editorevent', this, e);
14860       //  this.updateToolbar();
14861         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
14862     },
14863
14864     insertTag : function(tg)
14865     {
14866         // could be a bit smarter... -> wrap the current selected tRoo..
14867         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
14868             
14869             range = this.createRange(this.getSelection());
14870             var wrappingNode = this.doc.createElement(tg.toLowerCase());
14871             wrappingNode.appendChild(range.extractContents());
14872             range.insertNode(wrappingNode);
14873
14874             return;
14875             
14876             
14877             
14878         }
14879         this.execCmd("formatblock",   tg);
14880         
14881     },
14882     
14883     insertText : function(txt)
14884     {
14885         
14886         
14887         var range = this.createRange();
14888         range.deleteContents();
14889                //alert(Sender.getAttribute('label'));
14890                
14891         range.insertNode(this.doc.createTextNode(txt));
14892     } ,
14893     
14894      
14895
14896     /**
14897      * Executes a Midas editor command on the editor document and performs necessary focus and
14898      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
14899      * @param {String} cmd The Midas command
14900      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
14901      */
14902     relayCmd : function(cmd, value){
14903         this.win.focus();
14904         this.execCmd(cmd, value);
14905         this.owner.fireEvent('editorevent', this);
14906         //this.updateToolbar();
14907         this.owner.deferFocus();
14908     },
14909
14910     /**
14911      * Executes a Midas editor command directly on the editor document.
14912      * For visual commands, you should use {@link #relayCmd} instead.
14913      * <b>This should only be called after the editor is initialized.</b>
14914      * @param {String} cmd The Midas command
14915      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
14916      */
14917     execCmd : function(cmd, value){
14918         this.doc.execCommand(cmd, false, value === undefined ? null : value);
14919         this.syncValue();
14920     },
14921  
14922  
14923    
14924     /**
14925      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
14926      * to insert tRoo.
14927      * @param {String} text | dom node.. 
14928      */
14929     insertAtCursor : function(text)
14930     {
14931         
14932         
14933         
14934         if(!this.activated){
14935             return;
14936         }
14937         /*
14938         if(Roo.isIE){
14939             this.win.focus();
14940             var r = this.doc.selection.createRange();
14941             if(r){
14942                 r.collapse(true);
14943                 r.pasteHTML(text);
14944                 this.syncValue();
14945                 this.deferFocus();
14946             
14947             }
14948             return;
14949         }
14950         */
14951         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
14952             this.win.focus();
14953             
14954             
14955             // from jquery ui (MIT licenced)
14956             var range, node;
14957             var win = this.win;
14958             
14959             if (win.getSelection && win.getSelection().getRangeAt) {
14960                 range = win.getSelection().getRangeAt(0);
14961                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
14962                 range.insertNode(node);
14963             } else if (win.document.selection && win.document.selection.createRange) {
14964                 // no firefox support
14965                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
14966                 win.document.selection.createRange().pasteHTML(txt);
14967             } else {
14968                 // no firefox support
14969                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
14970                 this.execCmd('InsertHTML', txt);
14971             } 
14972             
14973             this.syncValue();
14974             
14975             this.deferFocus();
14976         }
14977     },
14978  // private
14979     mozKeyPress : function(e){
14980         if(e.ctrlKey){
14981             var c = e.getCharCode(), cmd;
14982           
14983             if(c > 0){
14984                 c = String.fromCharCode(c).toLowerCase();
14985                 switch(c){
14986                     case 'b':
14987                         cmd = 'bold';
14988                         break;
14989                     case 'i':
14990                         cmd = 'italic';
14991                         break;
14992                     
14993                     case 'u':
14994                         cmd = 'underline';
14995                         break;
14996                     
14997                     case 'v':
14998                         this.cleanUpPaste.defer(100, this);
14999                         return;
15000                         
15001                 }
15002                 if(cmd){
15003                     this.win.focus();
15004                     this.execCmd(cmd);
15005                     this.deferFocus();
15006                     e.preventDefault();
15007                 }
15008                 
15009             }
15010         }
15011     },
15012
15013     // private
15014     fixKeys : function(){ // load time branching for fastest keydown performance
15015         if(Roo.isIE){
15016             return function(e){
15017                 var k = e.getKey(), r;
15018                 if(k == e.TAB){
15019                     e.stopEvent();
15020                     r = this.doc.selection.createRange();
15021                     if(r){
15022                         r.collapse(true);
15023                         r.pasteHTML('&#160;&#160;&#160;&#160;');
15024                         this.deferFocus();
15025                     }
15026                     return;
15027                 }
15028                 
15029                 if(k == e.ENTER){
15030                     r = this.doc.selection.createRange();
15031                     if(r){
15032                         var target = r.parentElement();
15033                         if(!target || target.tagName.toLowerCase() != 'li'){
15034                             e.stopEvent();
15035                             r.pasteHTML('<br />');
15036                             r.collapse(false);
15037                             r.select();
15038                         }
15039                     }
15040                 }
15041                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
15042                     this.cleanUpPaste.defer(100, this);
15043                     return;
15044                 }
15045                 
15046                 
15047             };
15048         }else if(Roo.isOpera){
15049             return function(e){
15050                 var k = e.getKey();
15051                 if(k == e.TAB){
15052                     e.stopEvent();
15053                     this.win.focus();
15054                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
15055                     this.deferFocus();
15056                 }
15057                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
15058                     this.cleanUpPaste.defer(100, this);
15059                     return;
15060                 }
15061                 
15062             };
15063         }else if(Roo.isSafari){
15064             return function(e){
15065                 var k = e.getKey();
15066                 
15067                 if(k == e.TAB){
15068                     e.stopEvent();
15069                     this.execCmd('InsertText','\t');
15070                     this.deferFocus();
15071                     return;
15072                 }
15073                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
15074                     this.cleanUpPaste.defer(100, this);
15075                     return;
15076                 }
15077                 
15078              };
15079         }
15080     }(),
15081     
15082     getAllAncestors: function()
15083     {
15084         var p = this.getSelectedNode();
15085         var a = [];
15086         if (!p) {
15087             a.push(p); // push blank onto stack..
15088             p = this.getParentElement();
15089         }
15090         
15091         
15092         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
15093             a.push(p);
15094             p = p.parentNode;
15095         }
15096         a.push(this.doc.body);
15097         return a;
15098     },
15099     lastSel : false,
15100     lastSelNode : false,
15101     
15102     
15103     getSelection : function() 
15104     {
15105         this.assignDocWin();
15106         return Roo.isIE ? this.doc.selection : this.win.getSelection();
15107     },
15108     
15109     getSelectedNode: function() 
15110     {
15111         // this may only work on Gecko!!!
15112         
15113         // should we cache this!!!!
15114         
15115         
15116         
15117          
15118         var range = this.createRange(this.getSelection()).cloneRange();
15119         
15120         if (Roo.isIE) {
15121             var parent = range.parentElement();
15122             while (true) {
15123                 var testRange = range.duplicate();
15124                 testRange.moveToElementText(parent);
15125                 if (testRange.inRange(range)) {
15126                     break;
15127                 }
15128                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
15129                     break;
15130                 }
15131                 parent = parent.parentElement;
15132             }
15133             return parent;
15134         }
15135         
15136         // is ancestor a text element.
15137         var ac =  range.commonAncestorContainer;
15138         if (ac.nodeType == 3) {
15139             ac = ac.parentNode;
15140         }
15141         
15142         var ar = ac.childNodes;
15143          
15144         var nodes = [];
15145         var other_nodes = [];
15146         var has_other_nodes = false;
15147         for (var i=0;i<ar.length;i++) {
15148             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
15149                 continue;
15150             }
15151             // fullly contained node.
15152             
15153             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
15154                 nodes.push(ar[i]);
15155                 continue;
15156             }
15157             
15158             // probably selected..
15159             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
15160                 other_nodes.push(ar[i]);
15161                 continue;
15162             }
15163             // outer..
15164             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
15165                 continue;
15166             }
15167             
15168             
15169             has_other_nodes = true;
15170         }
15171         if (!nodes.length && other_nodes.length) {
15172             nodes= other_nodes;
15173         }
15174         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
15175             return false;
15176         }
15177         
15178         return nodes[0];
15179     },
15180     createRange: function(sel)
15181     {
15182         // this has strange effects when using with 
15183         // top toolbar - not sure if it's a great idea.
15184         //this.editor.contentWindow.focus();
15185         if (typeof sel != "undefined") {
15186             try {
15187                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
15188             } catch(e) {
15189                 return this.doc.createRange();
15190             }
15191         } else {
15192             return this.doc.createRange();
15193         }
15194     },
15195     getParentElement: function()
15196     {
15197         
15198         this.assignDocWin();
15199         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
15200         
15201         var range = this.createRange(sel);
15202          
15203         try {
15204             var p = range.commonAncestorContainer;
15205             while (p.nodeType == 3) { // text node
15206                 p = p.parentNode;
15207             }
15208             return p;
15209         } catch (e) {
15210             return null;
15211         }
15212     
15213     },
15214     /***
15215      *
15216      * Range intersection.. the hard stuff...
15217      *  '-1' = before
15218      *  '0' = hits..
15219      *  '1' = after.
15220      *         [ -- selected range --- ]
15221      *   [fail]                        [fail]
15222      *
15223      *    basically..
15224      *      if end is before start or  hits it. fail.
15225      *      if start is after end or hits it fail.
15226      *
15227      *   if either hits (but other is outside. - then it's not 
15228      *   
15229      *    
15230      **/
15231     
15232     
15233     // @see http://www.thismuchiknow.co.uk/?p=64.
15234     rangeIntersectsNode : function(range, node)
15235     {
15236         var nodeRange = node.ownerDocument.createRange();
15237         try {
15238             nodeRange.selectNode(node);
15239         } catch (e) {
15240             nodeRange.selectNodeContents(node);
15241         }
15242     
15243         var rangeStartRange = range.cloneRange();
15244         rangeStartRange.collapse(true);
15245     
15246         var rangeEndRange = range.cloneRange();
15247         rangeEndRange.collapse(false);
15248     
15249         var nodeStartRange = nodeRange.cloneRange();
15250         nodeStartRange.collapse(true);
15251     
15252         var nodeEndRange = nodeRange.cloneRange();
15253         nodeEndRange.collapse(false);
15254     
15255         return rangeStartRange.compareBoundaryPoints(
15256                  Range.START_TO_START, nodeEndRange) == -1 &&
15257                rangeEndRange.compareBoundaryPoints(
15258                  Range.START_TO_START, nodeStartRange) == 1;
15259         
15260          
15261     },
15262     rangeCompareNode : function(range, node)
15263     {
15264         var nodeRange = node.ownerDocument.createRange();
15265         try {
15266             nodeRange.selectNode(node);
15267         } catch (e) {
15268             nodeRange.selectNodeContents(node);
15269         }
15270         
15271         
15272         range.collapse(true);
15273     
15274         nodeRange.collapse(true);
15275      
15276         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
15277         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
15278          
15279         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
15280         
15281         var nodeIsBefore   =  ss == 1;
15282         var nodeIsAfter    = ee == -1;
15283         
15284         if (nodeIsBefore && nodeIsAfter)
15285             return 0; // outer
15286         if (!nodeIsBefore && nodeIsAfter)
15287             return 1; //right trailed.
15288         
15289         if (nodeIsBefore && !nodeIsAfter)
15290             return 2;  // left trailed.
15291         // fully contined.
15292         return 3;
15293     },
15294
15295     // private? - in a new class?
15296     cleanUpPaste :  function()
15297     {
15298         // cleans up the whole document..
15299         Roo.log('cleanuppaste');
15300         
15301         this.cleanUpChildren(this.doc.body);
15302         var clean = this.cleanWordChars(this.doc.body.innerHTML);
15303         if (clean != this.doc.body.innerHTML) {
15304             this.doc.body.innerHTML = clean;
15305         }
15306         
15307     },
15308     
15309     cleanWordChars : function(input) {// change the chars to hex code
15310         var he = Roo.HtmlEditorCore;
15311         
15312         var output = input;
15313         Roo.each(he.swapCodes, function(sw) { 
15314             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
15315             
15316             output = output.replace(swapper, sw[1]);
15317         });
15318         
15319         return output;
15320     },
15321     
15322     
15323     cleanUpChildren : function (n)
15324     {
15325         if (!n.childNodes.length) {
15326             return;
15327         }
15328         for (var i = n.childNodes.length-1; i > -1 ; i--) {
15329            this.cleanUpChild(n.childNodes[i]);
15330         }
15331     },
15332     
15333     
15334         
15335     
15336     cleanUpChild : function (node)
15337     {
15338         var ed = this;
15339         //console.log(node);
15340         if (node.nodeName == "#text") {
15341             // clean up silly Windows -- stuff?
15342             return; 
15343         }
15344         if (node.nodeName == "#comment") {
15345             node.parentNode.removeChild(node);
15346             // clean up silly Windows -- stuff?
15347             return; 
15348         }
15349         
15350         if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1 && this.clearUp) {
15351             // remove node.
15352             node.parentNode.removeChild(node);
15353             return;
15354             
15355         }
15356         
15357         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
15358         
15359         // remove <a name=....> as rendering on yahoo mailer is borked with this.
15360         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
15361         
15362         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
15363         //    remove_keep_children = true;
15364         //}
15365         
15366         if (remove_keep_children) {
15367             this.cleanUpChildren(node);
15368             // inserts everything just before this node...
15369             while (node.childNodes.length) {
15370                 var cn = node.childNodes[0];
15371                 node.removeChild(cn);
15372                 node.parentNode.insertBefore(cn, node);
15373             }
15374             node.parentNode.removeChild(node);
15375             return;
15376         }
15377         
15378         if (!node.attributes || !node.attributes.length) {
15379             this.cleanUpChildren(node);
15380             return;
15381         }
15382         
15383         function cleanAttr(n,v)
15384         {
15385             
15386             if (v.match(/^\./) || v.match(/^\//)) {
15387                 return;
15388             }
15389             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
15390                 return;
15391             }
15392             if (v.match(/^#/)) {
15393                 return;
15394             }
15395 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
15396             node.removeAttribute(n);
15397             
15398         }
15399         
15400         function cleanStyle(n,v)
15401         {
15402             if (v.match(/expression/)) { //XSS?? should we even bother..
15403                 node.removeAttribute(n);
15404                 return;
15405             }
15406             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
15407             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
15408             
15409             
15410             var parts = v.split(/;/);
15411             var clean = [];
15412             
15413             Roo.each(parts, function(p) {
15414                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
15415                 if (!p.length) {
15416                     return true;
15417                 }
15418                 var l = p.split(':').shift().replace(/\s+/g,'');
15419                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
15420                 
15421                 if ( cblack.indexOf(l) > -1) {
15422 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
15423                     //node.removeAttribute(n);
15424                     return true;
15425                 }
15426                 //Roo.log()
15427                 // only allow 'c whitelisted system attributes'
15428                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
15429 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
15430                     //node.removeAttribute(n);
15431                     return true;
15432                 }
15433                 
15434                 
15435                  
15436                 
15437                 clean.push(p);
15438                 return true;
15439             });
15440             if (clean.length) { 
15441                 node.setAttribute(n, clean.join(';'));
15442             } else {
15443                 node.removeAttribute(n);
15444             }
15445             
15446         }
15447         
15448         
15449         for (var i = node.attributes.length-1; i > -1 ; i--) {
15450             var a = node.attributes[i];
15451             //console.log(a);
15452             
15453             if (a.name.toLowerCase().substr(0,2)=='on')  {
15454                 node.removeAttribute(a.name);
15455                 continue;
15456             }
15457             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
15458                 node.removeAttribute(a.name);
15459                 continue;
15460             }
15461             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
15462                 cleanAttr(a.name,a.value); // fixme..
15463                 continue;
15464             }
15465             if (a.name == 'style') {
15466                 cleanStyle(a.name,a.value);
15467                 continue;
15468             }
15469             /// clean up MS crap..
15470             // tecnically this should be a list of valid class'es..
15471             
15472             
15473             if (a.name == 'class') {
15474                 if (a.value.match(/^Mso/)) {
15475                     node.className = '';
15476                 }
15477                 
15478                 if (a.value.match(/body/)) {
15479                     node.className = '';
15480                 }
15481                 continue;
15482             }
15483             
15484             // style cleanup!?
15485             // class cleanup?
15486             
15487         }
15488         
15489         
15490         this.cleanUpChildren(node);
15491         
15492         
15493     },
15494     /**
15495      * Clean up MS wordisms...
15496      */
15497     cleanWord : function(node)
15498     {
15499         var _t = this;
15500         var cleanWordChildren = function()
15501         {
15502             if (!node.childNodes.length) {
15503                 return;
15504             }
15505             for (var i = node.childNodes.length-1; i > -1 ; i--) {
15506                _t.cleanWord(node.childNodes[i]);
15507             }
15508         }
15509         
15510         
15511         if (!node) {
15512             this.cleanWord(this.doc.body);
15513             return;
15514         }
15515         if (node.nodeName == "#text") {
15516             // clean up silly Windows -- stuff?
15517             return; 
15518         }
15519         if (node.nodeName == "#comment") {
15520             node.parentNode.removeChild(node);
15521             // clean up silly Windows -- stuff?
15522             return; 
15523         }
15524         
15525         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
15526             node.parentNode.removeChild(node);
15527             return;
15528         }
15529         
15530         // remove - but keep children..
15531         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
15532             while (node.childNodes.length) {
15533                 var cn = node.childNodes[0];
15534                 node.removeChild(cn);
15535                 node.parentNode.insertBefore(cn, node);
15536             }
15537             node.parentNode.removeChild(node);
15538             cleanWordChildren();
15539             return;
15540         }
15541         // clean styles
15542         if (node.className.length) {
15543             
15544             var cn = node.className.split(/\W+/);
15545             var cna = [];
15546             Roo.each(cn, function(cls) {
15547                 if (cls.match(/Mso[a-zA-Z]+/)) {
15548                     return;
15549                 }
15550                 cna.push(cls);
15551             });
15552             node.className = cna.length ? cna.join(' ') : '';
15553             if (!cna.length) {
15554                 node.removeAttribute("class");
15555             }
15556         }
15557         
15558         if (node.hasAttribute("lang")) {
15559             node.removeAttribute("lang");
15560         }
15561         
15562         if (node.hasAttribute("style")) {
15563             
15564             var styles = node.getAttribute("style").split(";");
15565             var nstyle = [];
15566             Roo.each(styles, function(s) {
15567                 if (!s.match(/:/)) {
15568                     return;
15569                 }
15570                 var kv = s.split(":");
15571                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
15572                     return;
15573                 }
15574                 // what ever is left... we allow.
15575                 nstyle.push(s);
15576             });
15577             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
15578             if (!nstyle.length) {
15579                 node.removeAttribute('style');
15580             }
15581         }
15582         
15583         cleanWordChildren();
15584         
15585         
15586     },
15587     domToHTML : function(currentElement, depth, nopadtext) {
15588         
15589             depth = depth || 0;
15590             nopadtext = nopadtext || false;
15591         
15592             if (!currentElement) {
15593                 return this.domToHTML(this.doc.body);
15594             }
15595             
15596             //Roo.log(currentElement);
15597             var j;
15598             var allText = false;
15599             var nodeName = currentElement.nodeName;
15600             var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
15601             
15602             if  (nodeName == '#text') {
15603                 return currentElement.nodeValue;
15604             }
15605             
15606             
15607             var ret = '';
15608             if (nodeName != 'BODY') {
15609                  
15610                 var i = 0;
15611                 // Prints the node tagName, such as <A>, <IMG>, etc
15612                 if (tagName) {
15613                     var attr = [];
15614                     for(i = 0; i < currentElement.attributes.length;i++) {
15615                         // quoting?
15616                         var aname = currentElement.attributes.item(i).name;
15617                         if (!currentElement.attributes.item(i).value.length) {
15618                             continue;
15619                         }
15620                         attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
15621                     }
15622                     
15623                     ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
15624                 } 
15625                 else {
15626                     
15627                     // eack
15628                 }
15629             } else {
15630                 tagName = false;
15631             }
15632             if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
15633                 return ret;
15634             }
15635             if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
15636                 nopadtext = true;
15637             }
15638             
15639             
15640             // Traverse the tree
15641             i = 0;
15642             var currentElementChild = currentElement.childNodes.item(i);
15643             var allText = true;
15644             var innerHTML  = '';
15645             lastnode = '';
15646             while (currentElementChild) {
15647                 // Formatting code (indent the tree so it looks nice on the screen)
15648                 var nopad = nopadtext;
15649                 if (lastnode == 'SPAN') {
15650                     nopad  = true;
15651                 }
15652                 // text
15653                 if  (currentElementChild.nodeName == '#text') {
15654                     var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
15655                     if (!nopad && toadd.length > 80) {
15656                         innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
15657                     }
15658                     innerHTML  += toadd;
15659                     
15660                     i++;
15661                     currentElementChild = currentElement.childNodes.item(i);
15662                     lastNode = '';
15663                     continue;
15664                 }
15665                 allText = false;
15666                 
15667                 innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
15668                     
15669                 // Recursively traverse the tree structure of the child node
15670                 innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
15671                 lastnode = currentElementChild.nodeName;
15672                 i++;
15673                 currentElementChild=currentElement.childNodes.item(i);
15674             }
15675             
15676             ret += innerHTML;
15677             
15678             if (!allText) {
15679                     // The remaining code is mostly for formatting the tree
15680                 ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
15681             }
15682             
15683             
15684             if (tagName) {
15685                 ret+= "</"+tagName+">";
15686             }
15687             return ret;
15688             
15689         }
15690     
15691     // hide stuff that is not compatible
15692     /**
15693      * @event blur
15694      * @hide
15695      */
15696     /**
15697      * @event change
15698      * @hide
15699      */
15700     /**
15701      * @event focus
15702      * @hide
15703      */
15704     /**
15705      * @event specialkey
15706      * @hide
15707      */
15708     /**
15709      * @cfg {String} fieldClass @hide
15710      */
15711     /**
15712      * @cfg {String} focusClass @hide
15713      */
15714     /**
15715      * @cfg {String} autoCreate @hide
15716      */
15717     /**
15718      * @cfg {String} inputType @hide
15719      */
15720     /**
15721      * @cfg {String} invalidClass @hide
15722      */
15723     /**
15724      * @cfg {String} invalidText @hide
15725      */
15726     /**
15727      * @cfg {String} msgFx @hide
15728      */
15729     /**
15730      * @cfg {String} validateOnBlur @hide
15731      */
15732 });
15733
15734 Roo.HtmlEditorCore.white = [
15735         'area', 'br', 'img', 'input', 'hr', 'wbr',
15736         
15737        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
15738        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
15739        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
15740        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
15741        'table',   'ul',         'xmp', 
15742        
15743        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
15744       'thead',   'tr', 
15745      
15746       'dir', 'menu', 'ol', 'ul', 'dl',
15747        
15748       'embed',  'object'
15749 ];
15750
15751
15752 Roo.HtmlEditorCore.black = [
15753     //    'embed',  'object', // enable - backend responsiblity to clean thiese
15754         'applet', // 
15755         'base',   'basefont', 'bgsound', 'blink',  'body', 
15756         'frame',  'frameset', 'head',    'html',   'ilayer', 
15757         'iframe', 'layer',  'link',     'meta',    'object',   
15758         'script', 'style' ,'title',  'xml' // clean later..
15759 ];
15760 Roo.HtmlEditorCore.clean = [
15761     'script', 'style', 'title', 'xml'
15762 ];
15763 Roo.HtmlEditorCore.remove = [
15764     'font'
15765 ];
15766 // attributes..
15767
15768 Roo.HtmlEditorCore.ablack = [
15769     'on'
15770 ];
15771     
15772 Roo.HtmlEditorCore.aclean = [ 
15773     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
15774 ];
15775
15776 // protocols..
15777 Roo.HtmlEditorCore.pwhite= [
15778         'http',  'https',  'mailto'
15779 ];
15780
15781 // white listed style attributes.
15782 Roo.HtmlEditorCore.cwhite= [
15783       //  'text-align', /// default is to allow most things..
15784       
15785          
15786 //        'font-size'//??
15787 ];
15788
15789 // black listed style attributes.
15790 Roo.HtmlEditorCore.cblack= [
15791       //  'font-size' -- this can be set by the project 
15792 ];
15793
15794
15795 Roo.HtmlEditorCore.swapCodes   =[ 
15796     [    8211, "--" ], 
15797     [    8212, "--" ], 
15798     [    8216,  "'" ],  
15799     [    8217, "'" ],  
15800     [    8220, '"' ],  
15801     [    8221, '"' ],  
15802     [    8226, "*" ],  
15803     [    8230, "..." ]
15804 ]; 
15805
15806     /*
15807  * - LGPL
15808  *
15809  * HtmlEditor
15810  * 
15811  */
15812
15813 /**
15814  * @class Roo.bootstrap.HtmlEditor
15815  * @extends Roo.bootstrap.TextArea
15816  * Bootstrap HtmlEditor class
15817
15818  * @constructor
15819  * Create a new HtmlEditor
15820  * @param {Object} config The config object
15821  */
15822
15823 Roo.bootstrap.HtmlEditor = function(config){
15824     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
15825     if (!this.toolbars) {
15826         this.toolbars = [];
15827     }
15828     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
15829     this.addEvents({
15830             /**
15831              * @event initialize
15832              * Fires when the editor is fully initialized (including the iframe)
15833              * @param {HtmlEditor} this
15834              */
15835             initialize: true,
15836             /**
15837              * @event activate
15838              * Fires when the editor is first receives the focus. Any insertion must wait
15839              * until after this event.
15840              * @param {HtmlEditor} this
15841              */
15842             activate: true,
15843              /**
15844              * @event beforesync
15845              * Fires before the textarea is updated with content from the editor iframe. Return false
15846              * to cancel the sync.
15847              * @param {HtmlEditor} this
15848              * @param {String} html
15849              */
15850             beforesync: true,
15851              /**
15852              * @event beforepush
15853              * Fires before the iframe editor is updated with content from the textarea. Return false
15854              * to cancel the push.
15855              * @param {HtmlEditor} this
15856              * @param {String} html
15857              */
15858             beforepush: true,
15859              /**
15860              * @event sync
15861              * Fires when the textarea is updated with content from the editor iframe.
15862              * @param {HtmlEditor} this
15863              * @param {String} html
15864              */
15865             sync: true,
15866              /**
15867              * @event push
15868              * Fires when the iframe editor is updated with content from the textarea.
15869              * @param {HtmlEditor} this
15870              * @param {String} html
15871              */
15872             push: true,
15873              /**
15874              * @event editmodechange
15875              * Fires when the editor switches edit modes
15876              * @param {HtmlEditor} this
15877              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
15878              */
15879             editmodechange: true,
15880             /**
15881              * @event editorevent
15882              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
15883              * @param {HtmlEditor} this
15884              */
15885             editorevent: true,
15886             /**
15887              * @event firstfocus
15888              * Fires when on first focus - needed by toolbars..
15889              * @param {HtmlEditor} this
15890              */
15891             firstfocus: true,
15892             /**
15893              * @event autosave
15894              * Auto save the htmlEditor value as a file into Events
15895              * @param {HtmlEditor} this
15896              */
15897             autosave: true,
15898             /**
15899              * @event savedpreview
15900              * preview the saved version of htmlEditor
15901              * @param {HtmlEditor} this
15902              */
15903             savedpreview: true
15904         });
15905 };
15906
15907
15908 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
15909     
15910     
15911       /**
15912      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
15913      */
15914     toolbars : false,
15915    
15916      /**
15917      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
15918      *                        Roo.resizable.
15919      */
15920     resizable : false,
15921      /**
15922      * @cfg {Number} height (in pixels)
15923      */   
15924     height: 300,
15925    /**
15926      * @cfg {Number} width (in pixels)
15927      */   
15928     width: false,
15929     
15930     /**
15931      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
15932      * 
15933      */
15934     stylesheets: false,
15935     
15936     // id of frame..
15937     frameId: false,
15938     
15939     // private properties
15940     validationEvent : false,
15941     deferHeight: true,
15942     initialized : false,
15943     activated : false,
15944     
15945     onFocus : Roo.emptyFn,
15946     iframePad:3,
15947     hideMode:'offsets',
15948     
15949     
15950     tbContainer : false,
15951     
15952     toolbarContainer :function() {
15953         return this.wrap.select('.x-html-editor-tb',true).first();
15954     },
15955
15956     /**
15957      * Protected method that will not generally be called directly. It
15958      * is called when the editor creates its toolbar. Override this method if you need to
15959      * add custom toolbar buttons.
15960      * @param {HtmlEditor} editor
15961      */
15962     createToolbar : function(){
15963         
15964         Roo.log("create toolbars");
15965         
15966         this.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard({editor: this} ) ];
15967         this.toolbars[0].render(this.toolbarContainer());
15968         
15969         return;
15970         
15971 //        if (!editor.toolbars || !editor.toolbars.length) {
15972 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
15973 //        }
15974 //        
15975 //        for (var i =0 ; i < editor.toolbars.length;i++) {
15976 //            editor.toolbars[i] = Roo.factory(
15977 //                    typeof(editor.toolbars[i]) == 'string' ?
15978 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
15979 //                Roo.bootstrap.HtmlEditor);
15980 //            editor.toolbars[i].init(editor);
15981 //        }
15982     },
15983
15984      
15985     // private
15986     onRender : function(ct, position)
15987     {
15988        // Roo.log("Call onRender: " + this.xtype);
15989         var _t = this;
15990         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
15991       
15992         this.wrap = this.inputEl().wrap({
15993             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
15994         });
15995         
15996         this.editorcore.onRender(ct, position);
15997          
15998         if (this.resizable) {
15999             this.resizeEl = new Roo.Resizable(this.wrap, {
16000                 pinned : true,
16001                 wrap: true,
16002                 dynamic : true,
16003                 minHeight : this.height,
16004                 height: this.height,
16005                 handles : this.resizable,
16006                 width: this.width,
16007                 listeners : {
16008                     resize : function(r, w, h) {
16009                         _t.onResize(w,h); // -something
16010                     }
16011                 }
16012             });
16013             
16014         }
16015         this.createToolbar(this);
16016        
16017         
16018         if(!this.width && this.resizable){
16019             this.setSize(this.wrap.getSize());
16020         }
16021         if (this.resizeEl) {
16022             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
16023             // should trigger onReize..
16024         }
16025         
16026     },
16027
16028     // private
16029     onResize : function(w, h)
16030     {
16031         Roo.log('resize: ' +w + ',' + h );
16032         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
16033         var ew = false;
16034         var eh = false;
16035         
16036         if(this.inputEl() ){
16037             if(typeof w == 'number'){
16038                 var aw = w - this.wrap.getFrameWidth('lr');
16039                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
16040                 ew = aw;
16041             }
16042             if(typeof h == 'number'){
16043                  var tbh = -11;  // fixme it needs to tool bar size!
16044                 for (var i =0; i < this.toolbars.length;i++) {
16045                     // fixme - ask toolbars for heights?
16046                     tbh += this.toolbars[i].el.getHeight();
16047                     //if (this.toolbars[i].footer) {
16048                     //    tbh += this.toolbars[i].footer.el.getHeight();
16049                     //}
16050                 }
16051               
16052                 
16053                 
16054                 
16055                 
16056                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
16057                 ah -= 5; // knock a few pixes off for look..
16058                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
16059                 var eh = ah;
16060             }
16061         }
16062         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
16063         this.editorcore.onResize(ew,eh);
16064         
16065     },
16066
16067     /**
16068      * Toggles the editor between standard and source edit mode.
16069      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
16070      */
16071     toggleSourceEdit : function(sourceEditMode)
16072     {
16073         this.editorcore.toggleSourceEdit(sourceEditMode);
16074         
16075         if(this.editorcore.sourceEditMode){
16076             Roo.log('editor - showing textarea');
16077             
16078 //            Roo.log('in');
16079 //            Roo.log(this.syncValue());
16080             this.syncValue();
16081             this.inputEl().removeClass('hide');
16082             this.inputEl().dom.removeAttribute('tabIndex');
16083             this.inputEl().focus();
16084         }else{
16085             Roo.log('editor - hiding textarea');
16086 //            Roo.log('out')
16087 //            Roo.log(this.pushValue()); 
16088             this.pushValue();
16089             
16090             this.inputEl().addClass('hide');
16091             this.inputEl().dom.setAttribute('tabIndex', -1);
16092             //this.deferFocus();
16093         }
16094          
16095         if(this.resizable){
16096             this.setSize(this.wrap.getSize());
16097         }
16098         
16099         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
16100     },
16101  
16102     // private (for BoxComponent)
16103     adjustSize : Roo.BoxComponent.prototype.adjustSize,
16104
16105     // private (for BoxComponent)
16106     getResizeEl : function(){
16107         return this.wrap;
16108     },
16109
16110     // private (for BoxComponent)
16111     getPositionEl : function(){
16112         return this.wrap;
16113     },
16114
16115     // private
16116     initEvents : function(){
16117         this.originalValue = this.getValue();
16118     },
16119
16120 //    /**
16121 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
16122 //     * @method
16123 //     */
16124 //    markInvalid : Roo.emptyFn,
16125 //    /**
16126 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
16127 //     * @method
16128 //     */
16129 //    clearInvalid : Roo.emptyFn,
16130
16131     setValue : function(v){
16132         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
16133         this.editorcore.pushValue();
16134     },
16135
16136      
16137     // private
16138     deferFocus : function(){
16139         this.focus.defer(10, this);
16140     },
16141
16142     // doc'ed in Field
16143     focus : function(){
16144         this.editorcore.focus();
16145         
16146     },
16147       
16148
16149     // private
16150     onDestroy : function(){
16151         
16152         
16153         
16154         if(this.rendered){
16155             
16156             for (var i =0; i < this.toolbars.length;i++) {
16157                 // fixme - ask toolbars for heights?
16158                 this.toolbars[i].onDestroy();
16159             }
16160             
16161             this.wrap.dom.innerHTML = '';
16162             this.wrap.remove();
16163         }
16164     },
16165
16166     // private
16167     onFirstFocus : function(){
16168         //Roo.log("onFirstFocus");
16169         this.editorcore.onFirstFocus();
16170          for (var i =0; i < this.toolbars.length;i++) {
16171             this.toolbars[i].onFirstFocus();
16172         }
16173         
16174     },
16175     
16176     // private
16177     syncValue : function()
16178     {   
16179         this.editorcore.syncValue();
16180     },
16181     
16182     pushValue : function()
16183     {   
16184         this.editorcore.pushValue();
16185     }
16186      
16187     
16188     // hide stuff that is not compatible
16189     /**
16190      * @event blur
16191      * @hide
16192      */
16193     /**
16194      * @event change
16195      * @hide
16196      */
16197     /**
16198      * @event focus
16199      * @hide
16200      */
16201     /**
16202      * @event specialkey
16203      * @hide
16204      */
16205     /**
16206      * @cfg {String} fieldClass @hide
16207      */
16208     /**
16209      * @cfg {String} focusClass @hide
16210      */
16211     /**
16212      * @cfg {String} autoCreate @hide
16213      */
16214     /**
16215      * @cfg {String} inputType @hide
16216      */
16217     /**
16218      * @cfg {String} invalidClass @hide
16219      */
16220     /**
16221      * @cfg {String} invalidText @hide
16222      */
16223     /**
16224      * @cfg {String} msgFx @hide
16225      */
16226     /**
16227      * @cfg {String} validateOnBlur @hide
16228      */
16229 });
16230  
16231     
16232    
16233    
16234    
16235       
16236
16237 /**
16238  * @class Roo.bootstrap.HtmlEditorToolbar1
16239  * Basic Toolbar
16240  * 
16241  * Usage:
16242  *
16243  new Roo.bootstrap.HtmlEditor({
16244     ....
16245     toolbars : [
16246         new Roo.bootstrap.HtmlEditorToolbar1({
16247             disable : { fonts: 1 , format: 1, ..., ... , ...],
16248             btns : [ .... ]
16249         })
16250     }
16251      
16252  * 
16253  * @cfg {Object} disable List of elements to disable..
16254  * @cfg {Array} btns List of additional buttons.
16255  * 
16256  * 
16257  * NEEDS Extra CSS? 
16258  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
16259  */
16260  
16261 Roo.bootstrap.HtmlEditor.ToolbarStandard = function(config)
16262 {
16263     
16264     Roo.apply(this, config);
16265     
16266     // default disabled, based on 'good practice'..
16267     this.disable = this.disable || {};
16268     Roo.applyIf(this.disable, {
16269         fontSize : true,
16270         colors : true,
16271         specialElements : true
16272     });
16273     Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.constructor.call(this, config);
16274     
16275     this.editor = config.editor;
16276     this.editorcore = config.editor.editorcore;
16277     
16278     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
16279     
16280     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
16281     // dont call parent... till later.
16282 }
16283 Roo.extend(Roo.bootstrap.HtmlEditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
16284     
16285     
16286     bar : true,
16287     
16288     editor : false,
16289     editorcore : false,
16290     
16291     
16292     formats : [
16293         "p" ,  
16294         "h1","h2","h3","h4","h5","h6", 
16295         "pre", "code", 
16296         "abbr", "acronym", "address", "cite", "samp", "var",
16297         'div','span'
16298     ],
16299     
16300     onRender : function(ct, position)
16301     {
16302        // Roo.log("Call onRender: " + this.xtype);
16303         
16304        Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
16305        Roo.log(this.el);
16306        this.el.dom.style.marginBottom = '0';
16307        var _this = this;
16308        var editorcore = this.editorcore;
16309        var editor= this.editor;
16310        
16311        var children = [];
16312        var btn = function(id,cmd , toggle, handler){
16313        
16314             var  event = toggle ? 'toggle' : 'click';
16315        
16316             var a = {
16317                 size : 'sm',
16318                 xtype: 'Button',
16319                 xns: Roo.bootstrap,
16320                 glyphicon : id,
16321                 cmd : id || cmd,
16322                 enableToggle:toggle !== false,
16323                 //html : 'submit'
16324                 pressed : toggle ? false : null,
16325                 listeners : {}
16326             }
16327             a.listeners[toggle ? 'toggle' : 'click'] = function() {
16328                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
16329             }
16330             children.push(a);
16331             return a;
16332        }
16333         
16334         var style = {
16335                 xtype: 'Button',
16336                 size : 'sm',
16337                 xns: Roo.bootstrap,
16338                 glyphicon : 'font',
16339                 //html : 'submit'
16340                 menu : {
16341                     xtype: 'Menu',
16342                     xns: Roo.bootstrap,
16343                     items:  []
16344                 }
16345         };
16346         Roo.each(this.formats, function(f) {
16347             style.menu.items.push({
16348                 xtype :'MenuItem',
16349                 xns: Roo.bootstrap,
16350                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
16351                 tagname : f,
16352                 listeners : {
16353                     click : function()
16354                     {
16355                         editorcore.insertTag(this.tagname);
16356                         editor.focus();
16357                     }
16358                 }
16359                 
16360             });
16361         });
16362          children.push(style);   
16363             
16364             
16365         btn('bold',false,true);
16366         btn('italic',false,true);
16367         btn('align-left', 'justifyleft',true);
16368         btn('align-center', 'justifycenter',true);
16369         btn('align-right' , 'justifyright',true);
16370         btn('link', false, false, function(btn) {
16371             //Roo.log("create link?");
16372             var url = prompt(this.createLinkText, this.defaultLinkValue);
16373             if(url && url != 'http:/'+'/'){
16374                 this.editorcore.relayCmd('createlink', url);
16375             }
16376         }),
16377         btn('list','insertunorderedlist',true);
16378         btn('pencil', false,true, function(btn){
16379                 Roo.log(this);
16380                 
16381                 this.toggleSourceEdit(btn.pressed);
16382         });
16383         /*
16384         var cog = {
16385                 xtype: 'Button',
16386                 size : 'sm',
16387                 xns: Roo.bootstrap,
16388                 glyphicon : 'cog',
16389                 //html : 'submit'
16390                 menu : {
16391                     xtype: 'Menu',
16392                     xns: Roo.bootstrap,
16393                     items:  []
16394                 }
16395         };
16396         
16397         cog.menu.items.push({
16398             xtype :'MenuItem',
16399             xns: Roo.bootstrap,
16400             html : Clean styles,
16401             tagname : f,
16402             listeners : {
16403                 click : function()
16404                 {
16405                     editorcore.insertTag(this.tagname);
16406                     editor.focus();
16407                 }
16408             }
16409             
16410         });
16411        */
16412         
16413          
16414        this.xtype = 'NavSimplebar';
16415         
16416         for(var i=0;i< children.length;i++) {
16417             
16418             this.buttons.add(this.addxtypeChild(children[i]));
16419             
16420         }
16421         
16422         editor.on('editorevent', this.updateToolbar, this);
16423     },
16424     onBtnClick : function(id)
16425     {
16426        this.editorcore.relayCmd(id);
16427        this.editorcore.focus();
16428     },
16429     
16430     /**
16431      * Protected method that will not generally be called directly. It triggers
16432      * a toolbar update by reading the markup state of the current selection in the editor.
16433      */
16434     updateToolbar: function(){
16435
16436         if(!this.editorcore.activated){
16437             this.editor.onFirstFocus(); // is this neeed?
16438             return;
16439         }
16440
16441         var btns = this.buttons; 
16442         var doc = this.editorcore.doc;
16443         btns.get('bold').setActive(doc.queryCommandState('bold'));
16444         btns.get('italic').setActive(doc.queryCommandState('italic'));
16445         //btns.get('underline').setActive(doc.queryCommandState('underline'));
16446         
16447         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
16448         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
16449         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
16450         
16451         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
16452         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
16453          /*
16454         
16455         var ans = this.editorcore.getAllAncestors();
16456         if (this.formatCombo) {
16457             
16458             
16459             var store = this.formatCombo.store;
16460             this.formatCombo.setValue("");
16461             for (var i =0; i < ans.length;i++) {
16462                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
16463                     // select it..
16464                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
16465                     break;
16466                 }
16467             }
16468         }
16469         
16470         
16471         
16472         // hides menus... - so this cant be on a menu...
16473         Roo.bootstrap.MenuMgr.hideAll();
16474         */
16475         Roo.bootstrap.MenuMgr.hideAll();
16476         //this.editorsyncValue();
16477     },
16478     onFirstFocus: function() {
16479         this.buttons.each(function(item){
16480            item.enable();
16481         });
16482     },
16483     toggleSourceEdit : function(sourceEditMode){
16484         
16485           
16486         if(sourceEditMode){
16487             Roo.log("disabling buttons");
16488            this.buttons.each( function(item){
16489                 if(item.cmd != 'pencil'){
16490                     item.disable();
16491                 }
16492             });
16493           
16494         }else{
16495             Roo.log("enabling buttons");
16496             if(this.editorcore.initialized){
16497                 this.buttons.each( function(item){
16498                     item.enable();
16499                 });
16500             }
16501             
16502         }
16503         Roo.log("calling toggole on editor");
16504         // tell the editor that it's been pressed..
16505         this.editor.toggleSourceEdit(sourceEditMode);
16506        
16507     }
16508 });
16509
16510
16511
16512
16513
16514 /**
16515  * @class Roo.bootstrap.Table.AbstractSelectionModel
16516  * @extends Roo.util.Observable
16517  * Abstract base class for grid SelectionModels.  It provides the interface that should be
16518  * implemented by descendant classes.  This class should not be directly instantiated.
16519  * @constructor
16520  */
16521 Roo.bootstrap.Table.AbstractSelectionModel = function(){
16522     this.locked = false;
16523     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
16524 };
16525
16526
16527 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
16528     /** @ignore Called by the grid automatically. Do not call directly. */
16529     init : function(grid){
16530         this.grid = grid;
16531         this.initEvents();
16532     },
16533
16534     /**
16535      * Locks the selections.
16536      */
16537     lock : function(){
16538         this.locked = true;
16539     },
16540
16541     /**
16542      * Unlocks the selections.
16543      */
16544     unlock : function(){
16545         this.locked = false;
16546     },
16547
16548     /**
16549      * Returns true if the selections are locked.
16550      * @return {Boolean}
16551      */
16552     isLocked : function(){
16553         return this.locked;
16554     }
16555 });
16556 /**
16557  * @class Roo.bootstrap.Table.ColumnModel
16558  * @extends Roo.util.Observable
16559  * This is the default implementation of a ColumnModel used by the bootstrap table. It defines
16560  * the columns in the table.
16561  
16562  * @constructor
16563  * @param {Object} config An Array of column config objects. See this class's
16564  * config objects for details.
16565 */
16566 Roo.bootstrap.Table.ColumnModel = function(config){
16567         /**
16568      * The config passed into the constructor
16569      */
16570     this.config = config;
16571     this.lookup = {};
16572
16573     // if no id, create one
16574     // if the column does not have a dataIndex mapping,
16575     // map it to the order it is in the config
16576     for(var i = 0, len = config.length; i < len; i++){
16577         var c = config[i];
16578         if(typeof c.dataIndex == "undefined"){
16579             c.dataIndex = i;
16580         }
16581         if(typeof c.renderer == "string"){
16582             c.renderer = Roo.util.Format[c.renderer];
16583         }
16584         if(typeof c.id == "undefined"){
16585             c.id = Roo.id();
16586         }
16587 //        if(c.editor && c.editor.xtype){
16588 //            c.editor  = Roo.factory(c.editor, Roo.grid);
16589 //        }
16590 //        if(c.editor && c.editor.isFormField){
16591 //            c.editor = new Roo.grid.GridEditor(c.editor);
16592 //        }
16593
16594         this.lookup[c.id] = c;
16595     }
16596
16597     /**
16598      * The width of columns which have no width specified (defaults to 100)
16599      * @type Number
16600      */
16601     this.defaultWidth = 100;
16602
16603     /**
16604      * Default sortable of columns which have no sortable specified (defaults to false)
16605      * @type Boolean
16606      */
16607     this.defaultSortable = false;
16608
16609     this.addEvents({
16610         /**
16611              * @event widthchange
16612              * Fires when the width of a column changes.
16613              * @param {ColumnModel} this
16614              * @param {Number} columnIndex The column index
16615              * @param {Number} newWidth The new width
16616              */
16617             "widthchange": true,
16618         /**
16619              * @event headerchange
16620              * Fires when the text of a header changes.
16621              * @param {ColumnModel} this
16622              * @param {Number} columnIndex The column index
16623              * @param {Number} newText The new header text
16624              */
16625             "headerchange": true,
16626         /**
16627              * @event hiddenchange
16628              * Fires when a column is hidden or "unhidden".
16629              * @param {ColumnModel} this
16630              * @param {Number} columnIndex The column index
16631              * @param {Boolean} hidden true if hidden, false otherwise
16632              */
16633             "hiddenchange": true,
16634             /**
16635          * @event columnmoved
16636          * Fires when a column is moved.
16637          * @param {ColumnModel} this
16638          * @param {Number} oldIndex
16639          * @param {Number} newIndex
16640          */
16641         "columnmoved" : true,
16642         /**
16643          * @event columlockchange
16644          * Fires when a column's locked state is changed
16645          * @param {ColumnModel} this
16646          * @param {Number} colIndex
16647          * @param {Boolean} locked true if locked
16648          */
16649         "columnlockchange" : true
16650     });
16651     Roo.bootstrap.Table.ColumnModel.superclass.constructor.call(this);
16652 };
16653 Roo.extend(Roo.bootstrap.Table.ColumnModel, Roo.util.Observable, {
16654     /**
16655      * @cfg {String} header The header text to display in the Grid view.
16656      */
16657     /**
16658      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
16659      * {@link Roo.data.Record} definition from which to draw the column's value. If not
16660      * specified, the column's index is used as an index into the Record's data Array.
16661      */
16662     /**
16663      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
16664      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
16665      */
16666     /**
16667      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
16668      * Defaults to the value of the {@link #defaultSortable} property.
16669      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
16670      */
16671     /**
16672      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
16673      */
16674     /**
16675      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
16676      */
16677     /**
16678      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
16679      */
16680     /**
16681      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
16682      */
16683     /**
16684      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
16685      * given the cell's data value. See {@link #setRenderer}. If not specified, the
16686      * default renderer uses the raw data value.
16687      */
16688     /**
16689      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
16690      */
16691
16692     /**
16693      * Returns the id of the column at the specified index.
16694      * @param {Number} index The column index
16695      * @return {String} the id
16696      */
16697     getColumnId : function(index){
16698         return this.config[index].id;
16699     },
16700
16701     /**
16702      * Returns the column for a specified id.
16703      * @param {String} id The column id
16704      * @return {Object} the column
16705      */
16706     getColumnById : function(id){
16707         return this.lookup[id];
16708     },
16709
16710     
16711     /**
16712      * Returns the column for a specified dataIndex.
16713      * @param {String} dataIndex The column dataIndex
16714      * @return {Object|Boolean} the column or false if not found
16715      */
16716     getColumnByDataIndex: function(dataIndex){
16717         var index = this.findColumnIndex(dataIndex);
16718         return index > -1 ? this.config[index] : false;
16719     },
16720     
16721     /**
16722      * Returns the index for a specified column id.
16723      * @param {String} id The column id
16724      * @return {Number} the index, or -1 if not found
16725      */
16726     getIndexById : function(id){
16727         for(var i = 0, len = this.config.length; i < len; i++){
16728             if(this.config[i].id == id){
16729                 return i;
16730             }
16731         }
16732         return -1;
16733     },
16734     
16735     /**
16736      * Returns the index for a specified column dataIndex.
16737      * @param {String} dataIndex The column dataIndex
16738      * @return {Number} the index, or -1 if not found
16739      */
16740     
16741     findColumnIndex : function(dataIndex){
16742         for(var i = 0, len = this.config.length; i < len; i++){
16743             if(this.config[i].dataIndex == dataIndex){
16744                 return i;
16745             }
16746         }
16747         return -1;
16748     },
16749     
16750     
16751     moveColumn : function(oldIndex, newIndex){
16752         var c = this.config[oldIndex];
16753         this.config.splice(oldIndex, 1);
16754         this.config.splice(newIndex, 0, c);
16755         this.dataMap = null;
16756         this.fireEvent("columnmoved", this, oldIndex, newIndex);
16757     },
16758
16759     isLocked : function(colIndex){
16760         return this.config[colIndex].locked === true;
16761     },
16762
16763     setLocked : function(colIndex, value, suppressEvent){
16764         if(this.isLocked(colIndex) == value){
16765             return;
16766         }
16767         this.config[colIndex].locked = value;
16768         if(!suppressEvent){
16769             this.fireEvent("columnlockchange", this, colIndex, value);
16770         }
16771     },
16772
16773     getTotalLockedWidth : function(){
16774         var totalWidth = 0;
16775         for(var i = 0; i < this.config.length; i++){
16776             if(this.isLocked(i) && !this.isHidden(i)){
16777                 this.totalWidth += this.getColumnWidth(i);
16778             }
16779         }
16780         return totalWidth;
16781     },
16782
16783     getLockedCount : function(){
16784         for(var i = 0, len = this.config.length; i < len; i++){
16785             if(!this.isLocked(i)){
16786                 return i;
16787             }
16788         }
16789     },
16790
16791     /**
16792      * Returns the number of columns.
16793      * @return {Number}
16794      */
16795     getColumnCount : function(visibleOnly){
16796         if(visibleOnly === true){
16797             var c = 0;
16798             for(var i = 0, len = this.config.length; i < len; i++){
16799                 if(!this.isHidden(i)){
16800                     c++;
16801                 }
16802             }
16803             return c;
16804         }
16805         return this.config.length;
16806     },
16807
16808     /**
16809      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
16810      * @param {Function} fn
16811      * @param {Object} scope (optional)
16812      * @return {Array} result
16813      */
16814     getColumnsBy : function(fn, scope){
16815         var r = [];
16816         for(var i = 0, len = this.config.length; i < len; i++){
16817             var c = this.config[i];
16818             if(fn.call(scope||this, c, i) === true){
16819                 r[r.length] = c;
16820             }
16821         }
16822         return r;
16823     },
16824
16825     /**
16826      * Returns true if the specified column is sortable.
16827      * @param {Number} col The column index
16828      * @return {Boolean}
16829      */
16830     isSortable : function(col){
16831         if(typeof this.config[col].sortable == "undefined"){
16832             return this.defaultSortable;
16833         }
16834         return this.config[col].sortable;
16835     },
16836
16837     /**
16838      * Returns the rendering (formatting) function defined for the column.
16839      * @param {Number} col The column index.
16840      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
16841      */
16842     getRenderer : function(col){
16843         if(!this.config[col].renderer){
16844             return Roo.bootstrap.Table.ColumnModel.defaultRenderer;
16845         }
16846         return this.config[col].renderer;
16847     },
16848
16849     /**
16850      * Sets the rendering (formatting) function for a column.
16851      * @param {Number} col The column index
16852      * @param {Function} fn The function to use to process the cell's raw data
16853      * to return HTML markup for the grid view. The render function is called with
16854      * the following parameters:<ul>
16855      * <li>Data value.</li>
16856      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
16857      * <li>css A CSS style string to apply to the table cell.</li>
16858      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
16859      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
16860      * <li>Row index</li>
16861      * <li>Column index</li>
16862      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
16863      */
16864     setRenderer : function(col, fn){
16865         this.config[col].renderer = fn;
16866     },
16867
16868     /**
16869      * Returns the width for the specified column.
16870      * @param {Number} col The column index
16871      * @return {Number}
16872      */
16873     getColumnWidth : function(col){
16874         return this.config[col].width * 1 || this.defaultWidth;
16875     },
16876
16877     /**
16878      * Sets the width for a column.
16879      * @param {Number} col The column index
16880      * @param {Number} width The new width
16881      */
16882     setColumnWidth : function(col, width, suppressEvent){
16883         this.config[col].width = width;
16884         this.totalWidth = null;
16885         if(!suppressEvent){
16886              this.fireEvent("widthchange", this, col, width);
16887         }
16888     },
16889
16890     /**
16891      * Returns the total width of all columns.
16892      * @param {Boolean} includeHidden True to include hidden column widths
16893      * @return {Number}
16894      */
16895     getTotalWidth : function(includeHidden){
16896         if(!this.totalWidth){
16897             this.totalWidth = 0;
16898             for(var i = 0, len = this.config.length; i < len; i++){
16899                 if(includeHidden || !this.isHidden(i)){
16900                     this.totalWidth += this.getColumnWidth(i);
16901                 }
16902             }
16903         }
16904         return this.totalWidth;
16905     },
16906
16907     /**
16908      * Returns the header for the specified column.
16909      * @param {Number} col The column index
16910      * @return {String}
16911      */
16912     getColumnHeader : function(col){
16913         return this.config[col].header;
16914     },
16915
16916     /**
16917      * Sets the header for a column.
16918      * @param {Number} col The column index
16919      * @param {String} header The new header
16920      */
16921     setColumnHeader : function(col, header){
16922         this.config[col].header = header;
16923         this.fireEvent("headerchange", this, col, header);
16924     },
16925
16926     /**
16927      * Returns the tooltip for the specified column.
16928      * @param {Number} col The column index
16929      * @return {String}
16930      */
16931     getColumnTooltip : function(col){
16932             return this.config[col].tooltip;
16933     },
16934     /**
16935      * Sets the tooltip for a column.
16936      * @param {Number} col The column index
16937      * @param {String} tooltip The new tooltip
16938      */
16939     setColumnTooltip : function(col, tooltip){
16940             this.config[col].tooltip = tooltip;
16941     },
16942
16943     /**
16944      * Returns the dataIndex for the specified column.
16945      * @param {Number} col The column index
16946      * @return {Number}
16947      */
16948     getDataIndex : function(col){
16949         return this.config[col].dataIndex;
16950     },
16951
16952     /**
16953      * Sets the dataIndex for a column.
16954      * @param {Number} col The column index
16955      * @param {Number} dataIndex The new dataIndex
16956      */
16957     setDataIndex : function(col, dataIndex){
16958         this.config[col].dataIndex = dataIndex;
16959     },
16960
16961     
16962     
16963     /**
16964      * Returns true if the cell is editable.
16965      * @param {Number} colIndex The column index
16966      * @param {Number} rowIndex The row index
16967      * @return {Boolean}
16968      */
16969     isCellEditable : function(colIndex, rowIndex){
16970         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
16971     },
16972
16973     /**
16974      * Returns the editor defined for the cell/column.
16975      * return false or null to disable editing.
16976      * @param {Number} colIndex The column index
16977      * @param {Number} rowIndex The row index
16978      * @return {Object}
16979      */
16980     getCellEditor : function(colIndex, rowIndex){
16981         return this.config[colIndex].editor;
16982     },
16983
16984     /**
16985      * Sets if a column is editable.
16986      * @param {Number} col The column index
16987      * @param {Boolean} editable True if the column is editable
16988      */
16989     setEditable : function(col, editable){
16990         this.config[col].editable = editable;
16991     },
16992
16993
16994     /**
16995      * Returns true if the column is hidden.
16996      * @param {Number} colIndex The column index
16997      * @return {Boolean}
16998      */
16999     isHidden : function(colIndex){
17000         return this.config[colIndex].hidden;
17001     },
17002
17003
17004     /**
17005      * Returns true if the column width cannot be changed
17006      */
17007     isFixed : function(colIndex){
17008         return this.config[colIndex].fixed;
17009     },
17010
17011     /**
17012      * Returns true if the column can be resized
17013      * @return {Boolean}
17014      */
17015     isResizable : function(colIndex){
17016         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
17017     },
17018     /**
17019      * Sets if a column is hidden.
17020      * @param {Number} colIndex The column index
17021      * @param {Boolean} hidden True if the column is hidden
17022      */
17023     setHidden : function(colIndex, hidden){
17024         this.config[colIndex].hidden = hidden;
17025         this.totalWidth = null;
17026         this.fireEvent("hiddenchange", this, colIndex, hidden);
17027     },
17028
17029     /**
17030      * Sets the editor for a column.
17031      * @param {Number} col The column index
17032      * @param {Object} editor The editor object
17033      */
17034     setEditor : function(col, editor){
17035         this.config[col].editor = editor;
17036     }
17037 });
17038
17039 Roo.bootstrap.Table.ColumnModel.defaultRenderer = function(value){
17040         if(typeof value == "string" && value.length < 1){
17041             return "&#160;";
17042         }
17043         return value;
17044 };
17045
17046 // Alias for backwards compatibility
17047 Roo.bootstrap.Table.DefaultColumnModel = Roo.bootstrap.Table.ColumnModel;
17048
17049 /**
17050  * @extends Roo.bootstrap.Table.AbstractSelectionModel
17051  * @class Roo.bootstrap.Table.RowSelectionModel
17052  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
17053  * It supports multiple selections and keyboard selection/navigation. 
17054  * @constructor
17055  * @param {Object} config
17056  */
17057
17058 Roo.bootstrap.Table.RowSelectionModel = function(config){
17059     Roo.apply(this, config);
17060     this.selections = new Roo.util.MixedCollection(false, function(o){
17061         return o.id;
17062     });
17063
17064     this.last = false;
17065     this.lastActive = false;
17066
17067     this.addEvents({
17068         /**
17069              * @event selectionchange
17070              * Fires when the selection changes
17071              * @param {SelectionModel} this
17072              */
17073             "selectionchange" : true,
17074         /**
17075              * @event afterselectionchange
17076              * Fires after the selection changes (eg. by key press or clicking)
17077              * @param {SelectionModel} this
17078              */
17079             "afterselectionchange" : true,
17080         /**
17081              * @event beforerowselect
17082              * Fires when a row is selected being selected, return false to cancel.
17083              * @param {SelectionModel} this
17084              * @param {Number} rowIndex The selected index
17085              * @param {Boolean} keepExisting False if other selections will be cleared
17086              */
17087             "beforerowselect" : true,
17088         /**
17089              * @event rowselect
17090              * Fires when a row is selected.
17091              * @param {SelectionModel} this
17092              * @param {Number} rowIndex The selected index
17093              * @param {Roo.data.Record} r The record
17094              */
17095             "rowselect" : true,
17096         /**
17097              * @event rowdeselect
17098              * Fires when a row is deselected.
17099              * @param {SelectionModel} this
17100              * @param {Number} rowIndex The selected index
17101              */
17102         "rowdeselect" : true
17103     });
17104     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
17105     this.locked = false;
17106 };
17107
17108 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
17109     /**
17110      * @cfg {Boolean} singleSelect
17111      * True to allow selection of only one row at a time (defaults to false)
17112      */
17113     singleSelect : false,
17114
17115     // private
17116     initEvents : function(){
17117
17118         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
17119             this.grid.on("mousedown", this.handleMouseDown, this);
17120         }else{ // allow click to work like normal
17121             this.grid.on("rowclick", this.handleDragableRowClick, this);
17122         }
17123
17124         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
17125             "up" : function(e){
17126                 if(!e.shiftKey){
17127                     this.selectPrevious(e.shiftKey);
17128                 }else if(this.last !== false && this.lastActive !== false){
17129                     var last = this.last;
17130                     this.selectRange(this.last,  this.lastActive-1);
17131                     this.grid.getView().focusRow(this.lastActive);
17132                     if(last !== false){
17133                         this.last = last;
17134                     }
17135                 }else{
17136                     this.selectFirstRow();
17137                 }
17138                 this.fireEvent("afterselectionchange", this);
17139             },
17140             "down" : function(e){
17141                 if(!e.shiftKey){
17142                     this.selectNext(e.shiftKey);
17143                 }else if(this.last !== false && this.lastActive !== false){
17144                     var last = this.last;
17145                     this.selectRange(this.last,  this.lastActive+1);
17146                     this.grid.getView().focusRow(this.lastActive);
17147                     if(last !== false){
17148                         this.last = last;
17149                     }
17150                 }else{
17151                     this.selectFirstRow();
17152                 }
17153                 this.fireEvent("afterselectionchange", this);
17154             },
17155             scope: this
17156         });
17157
17158         var view = this.grid.view;
17159         view.on("refresh", this.onRefresh, this);
17160         view.on("rowupdated", this.onRowUpdated, this);
17161         view.on("rowremoved", this.onRemove, this);
17162     },
17163
17164     // private
17165     onRefresh : function(){
17166         var ds = this.grid.dataSource, i, v = this.grid.view;
17167         var s = this.selections;
17168         s.each(function(r){
17169             if((i = ds.indexOfId(r.id)) != -1){
17170                 v.onRowSelect(i);
17171             }else{
17172                 s.remove(r);
17173             }
17174         });
17175     },
17176
17177     // private
17178     onRemove : function(v, index, r){
17179         this.selections.remove(r);
17180     },
17181
17182     // private
17183     onRowUpdated : function(v, index, r){
17184         if(this.isSelected(r)){
17185             v.onRowSelect(index);
17186         }
17187     },
17188
17189     /**
17190      * Select records.
17191      * @param {Array} records The records to select
17192      * @param {Boolean} keepExisting (optional) True to keep existing selections
17193      */
17194     selectRecords : function(records, keepExisting){
17195         if(!keepExisting){
17196             this.clearSelections();
17197         }
17198         var ds = this.grid.dataSource;
17199         for(var i = 0, len = records.length; i < len; i++){
17200             this.selectRow(ds.indexOf(records[i]), true);
17201         }
17202     },
17203
17204     /**
17205      * Gets the number of selected rows.
17206      * @return {Number}
17207      */
17208     getCount : function(){
17209         return this.selections.length;
17210     },
17211
17212     /**
17213      * Selects the first row in the grid.
17214      */
17215     selectFirstRow : function(){
17216         this.selectRow(0);
17217     },
17218
17219     /**
17220      * Select the last row.
17221      * @param {Boolean} keepExisting (optional) True to keep existing selections
17222      */
17223     selectLastRow : function(keepExisting){
17224         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
17225     },
17226
17227     /**
17228      * Selects the row immediately following the last selected row.
17229      * @param {Boolean} keepExisting (optional) True to keep existing selections
17230      */
17231     selectNext : function(keepExisting){
17232         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
17233             this.selectRow(this.last+1, keepExisting);
17234             this.grid.getView().focusRow(this.last);
17235         }
17236     },
17237
17238     /**
17239      * Selects the row that precedes the last selected row.
17240      * @param {Boolean} keepExisting (optional) True to keep existing selections
17241      */
17242     selectPrevious : function(keepExisting){
17243         if(this.last){
17244             this.selectRow(this.last-1, keepExisting);
17245             this.grid.getView().focusRow(this.last);
17246         }
17247     },
17248
17249     /**
17250      * Returns the selected records
17251      * @return {Array} Array of selected records
17252      */
17253     getSelections : function(){
17254         return [].concat(this.selections.items);
17255     },
17256
17257     /**
17258      * Returns the first selected record.
17259      * @return {Record}
17260      */
17261     getSelected : function(){
17262         return this.selections.itemAt(0);
17263     },
17264
17265
17266     /**
17267      * Clears all selections.
17268      */
17269     clearSelections : function(fast){
17270         if(this.locked) return;
17271         if(fast !== true){
17272             var ds = this.grid.dataSource;
17273             var s = this.selections;
17274             s.each(function(r){
17275                 this.deselectRow(ds.indexOfId(r.id));
17276             }, this);
17277             s.clear();
17278         }else{
17279             this.selections.clear();
17280         }
17281         this.last = false;
17282     },
17283
17284
17285     /**
17286      * Selects all rows.
17287      */
17288     selectAll : function(){
17289         if(this.locked) return;
17290         this.selections.clear();
17291         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
17292             this.selectRow(i, true);
17293         }
17294     },
17295
17296     /**
17297      * Returns True if there is a selection.
17298      * @return {Boolean}
17299      */
17300     hasSelection : function(){
17301         return this.selections.length > 0;
17302     },
17303
17304     /**
17305      * Returns True if the specified row is selected.
17306      * @param {Number/Record} record The record or index of the record to check
17307      * @return {Boolean}
17308      */
17309     isSelected : function(index){
17310         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
17311         return (r && this.selections.key(r.id) ? true : false);
17312     },
17313
17314     /**
17315      * Returns True if the specified record id is selected.
17316      * @param {String} id The id of record to check
17317      * @return {Boolean}
17318      */
17319     isIdSelected : function(id){
17320         return (this.selections.key(id) ? true : false);
17321     },
17322
17323     // private
17324     handleMouseDown : function(e, t){
17325         var view = this.grid.getView(), rowIndex;
17326         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
17327             return;
17328         };
17329         if(e.shiftKey && this.last !== false){
17330             var last = this.last;
17331             this.selectRange(last, rowIndex, e.ctrlKey);
17332             this.last = last; // reset the last
17333             view.focusRow(rowIndex);
17334         }else{
17335             var isSelected = this.isSelected(rowIndex);
17336             if(e.button !== 0 && isSelected){
17337                 view.focusRow(rowIndex);
17338             }else if(e.ctrlKey && isSelected){
17339                 this.deselectRow(rowIndex);
17340             }else if(!isSelected){
17341                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
17342                 view.focusRow(rowIndex);
17343             }
17344         }
17345         this.fireEvent("afterselectionchange", this);
17346     },
17347     // private
17348     handleDragableRowClick :  function(grid, rowIndex, e) 
17349     {
17350         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
17351             this.selectRow(rowIndex, false);
17352             grid.view.focusRow(rowIndex);
17353              this.fireEvent("afterselectionchange", this);
17354         }
17355     },
17356     
17357     /**
17358      * Selects multiple rows.
17359      * @param {Array} rows Array of the indexes of the row to select
17360      * @param {Boolean} keepExisting (optional) True to keep existing selections
17361      */
17362     selectRows : function(rows, keepExisting){
17363         if(!keepExisting){
17364             this.clearSelections();
17365         }
17366         for(var i = 0, len = rows.length; i < len; i++){
17367             this.selectRow(rows[i], true);
17368         }
17369     },
17370
17371     /**
17372      * Selects a range of rows. All rows in between startRow and endRow are also selected.
17373      * @param {Number} startRow The index of the first row in the range
17374      * @param {Number} endRow The index of the last row in the range
17375      * @param {Boolean} keepExisting (optional) True to retain existing selections
17376      */
17377     selectRange : function(startRow, endRow, keepExisting){
17378         if(this.locked) return;
17379         if(!keepExisting){
17380             this.clearSelections();
17381         }
17382         if(startRow <= endRow){
17383             for(var i = startRow; i <= endRow; i++){
17384                 this.selectRow(i, true);
17385             }
17386         }else{
17387             for(var i = startRow; i >= endRow; i--){
17388                 this.selectRow(i, true);
17389             }
17390         }
17391     },
17392
17393     /**
17394      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
17395      * @param {Number} startRow The index of the first row in the range
17396      * @param {Number} endRow The index of the last row in the range
17397      */
17398     deselectRange : function(startRow, endRow, preventViewNotify){
17399         if(this.locked) return;
17400         for(var i = startRow; i <= endRow; i++){
17401             this.deselectRow(i, preventViewNotify);
17402         }
17403     },
17404
17405     /**
17406      * Selects a row.
17407      * @param {Number} row The index of the row to select
17408      * @param {Boolean} keepExisting (optional) True to keep existing selections
17409      */
17410     selectRow : function(index, keepExisting, preventViewNotify){
17411         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
17412         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
17413             if(!keepExisting || this.singleSelect){
17414                 this.clearSelections();
17415             }
17416             var r = this.grid.dataSource.getAt(index);
17417             this.selections.add(r);
17418             this.last = this.lastActive = index;
17419             if(!preventViewNotify){
17420                 this.grid.getView().onRowSelect(index);
17421             }
17422             this.fireEvent("rowselect", this, index, r);
17423             this.fireEvent("selectionchange", this);
17424         }
17425     },
17426
17427     /**
17428      * Deselects a row.
17429      * @param {Number} row The index of the row to deselect
17430      */
17431     deselectRow : function(index, preventViewNotify){
17432         if(this.locked) return;
17433         if(this.last == index){
17434             this.last = false;
17435         }
17436         if(this.lastActive == index){
17437             this.lastActive = false;
17438         }
17439         var r = this.grid.dataSource.getAt(index);
17440         this.selections.remove(r);
17441         if(!preventViewNotify){
17442             this.grid.getView().onRowDeselect(index);
17443         }
17444         this.fireEvent("rowdeselect", this, index);
17445         this.fireEvent("selectionchange", this);
17446     },
17447
17448     // private
17449     restoreLast : function(){
17450         if(this._last){
17451             this.last = this._last;
17452         }
17453     },
17454
17455     // private
17456     acceptsNav : function(row, col, cm){
17457         return !cm.isHidden(col) && cm.isCellEditable(col, row);
17458     },
17459
17460     // private
17461     onEditorKey : function(field, e){
17462         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
17463         if(k == e.TAB){
17464             e.stopEvent();
17465             ed.completeEdit();
17466             if(e.shiftKey){
17467                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
17468             }else{
17469                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
17470             }
17471         }else if(k == e.ENTER && !e.ctrlKey){
17472             e.stopEvent();
17473             ed.completeEdit();
17474             if(e.shiftKey){
17475                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
17476             }else{
17477                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
17478             }
17479         }else if(k == e.ESC){
17480             ed.cancelEdit();
17481         }
17482         if(newCell){
17483             g.startEditing(newCell[0], newCell[1]);
17484         }
17485     }
17486 });/*
17487  * - LGPL
17488  *
17489  * element
17490  * 
17491  */
17492
17493 /**
17494  * @class Roo.bootstrap.MessageBar
17495  * @extends Roo.bootstrap.Component
17496  * Bootstrap MessageBar class
17497  * @cfg {String} html contents of the MessageBar
17498  * @cfg {String} weight (info | success | warning | danger) default info
17499  * @cfg {String} beforeClass insert the bar before the given class
17500  * @cfg {Boolean} closable (true | false) default false
17501  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
17502  * 
17503  * @constructor
17504  * Create a new Element
17505  * @param {Object} config The config object
17506  */
17507
17508 Roo.bootstrap.MessageBar = function(config){
17509     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
17510 };
17511
17512 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
17513     
17514     html: '',
17515     weight: 'info',
17516     closable: false,
17517     fixed: false,
17518     beforeClass: 'bootstrap-sticky-wrap',
17519     
17520     getAutoCreate : function(){
17521         
17522         var cfg = {
17523             tag: 'div',
17524             cls: 'alert alert-dismissable alert-' + this.weight,
17525             cn: [
17526                 {
17527                     tag: 'span',
17528                     cls: 'message',
17529                     html: this.html || ''
17530                 }
17531             ]
17532         }
17533         
17534         if(this.fixed){
17535             cfg.cls += ' alert-messages-fixed';
17536         }
17537         
17538         if(this.closable){
17539             cfg.cn.push({
17540                 tag: 'button',
17541                 cls: 'close',
17542                 html: 'x'
17543             });
17544         }
17545         
17546         return cfg;
17547     },
17548     
17549     onRender : function(ct, position)
17550     {
17551         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17552         
17553         if(!this.el){
17554             var cfg = Roo.apply({},  this.getAutoCreate());
17555             cfg.id = Roo.id();
17556             
17557             if (this.cls) {
17558                 cfg.cls += ' ' + this.cls;
17559             }
17560             if (this.style) {
17561                 cfg.style = this.style;
17562             }
17563             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
17564             
17565             this.el.setVisibilityMode(Roo.Element.DISPLAY);
17566         }
17567         
17568         this.el.select('>button.close').on('click', this.hide, this);
17569         
17570     },
17571     
17572     show : function()
17573     {
17574         if (!this.rendered) {
17575             this.render();
17576         }
17577         
17578         this.el.show();
17579         
17580         this.fireEvent('show', this);
17581         
17582     },
17583     
17584     hide : function()
17585     {
17586         if (!this.rendered) {
17587             this.render();
17588         }
17589         
17590         this.el.hide();
17591         
17592         this.fireEvent('hide', this);
17593     },
17594     
17595     update : function()
17596     {
17597 //        var e = this.el.dom.firstChild;
17598 //        
17599 //        if(this.closable){
17600 //            e = e.nextSibling;
17601 //        }
17602 //        
17603 //        e.data = this.html || '';
17604
17605         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
17606     }
17607    
17608 });
17609
17610  
17611
17612