roojs-mailer.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%;margin:100 auto",
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, 300); // we will fix the height at the beginning...
4241         
4242         this.maskEl.enableDisplayMode("block");
4243         
4244         this.store.on('load', this.onLoad, this);
4245         this.store.on('beforeload', this.onBeforeLoad, this);
4246         
4247         this.store.load();
4248         
4249         
4250         
4251     },
4252     
4253     sort : function(e,el)
4254     {
4255         var col = Roo.get(el)
4256         
4257         if(!col.hasClass('sortable')){
4258             return;
4259         }
4260         
4261         var sort = col.attr('sort');
4262         var dir = 'ASC';
4263         
4264         if(col.hasClass('glyphicon-arrow-up')){
4265             dir = 'DESC';
4266         }
4267         
4268         this.store.sortInfo = {field : sort, direction : dir};
4269         
4270         this.store.load();
4271     },
4272     
4273     renderHeader : function()
4274     {
4275         var header = {
4276             tag: 'thead',
4277             cn : []
4278         };
4279         
4280         var cm = this.cm;
4281         
4282         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
4283             
4284             var config = cm.config[i];
4285             
4286             var c = {
4287                 tag: 'th',
4288                 html: cm.getColumnHeader(i)
4289             };
4290             
4291             if(typeof(config.dataIndex) != 'undefined'){
4292                 c.sort = config.dataIndex;
4293             }
4294             
4295             if(typeof(config.sortable) != 'undefined' && config.sortable){
4296                 c.cls = 'sortable';
4297             }
4298             
4299             if(typeof(config.width) != 'undefined'){
4300                 c.style = 'width:' + config.width + 'px';
4301             }
4302             
4303             header.cn.push(c)
4304         }
4305         
4306         return header;
4307     },
4308     
4309     renderBody : function()
4310     {
4311         var body = {
4312             tag: 'tbody',
4313             cn : []
4314         };
4315         
4316         return body;
4317     },
4318     
4319     renderFooter : function()
4320     {
4321         var footer = {
4322             tag: 'tfoot',
4323             cn : []
4324         };
4325         
4326         return footer;
4327     },
4328     
4329     onLoad : function()
4330     {
4331         Roo.log('ds onload');
4332         
4333         var _this = this;
4334         var cm = this.cm;
4335         
4336         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
4337             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
4338             
4339             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
4340                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
4341             }
4342             
4343             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
4344                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
4345             }
4346         });
4347         
4348         var tbody = this.el.select('tbody', true).first();
4349         
4350         var renders = [];
4351         
4352         if(this.store.getCount() > 0){
4353             this.store.data.each(function(d){
4354                 var row = {
4355                     tag : 'tr',
4356                     cn : []
4357                 };
4358                 
4359                 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
4360                     var renderer = cm.getRenderer(i);
4361                     var config = cm.config[i];
4362                     var value = '';
4363                     var id = Roo.id();
4364                     
4365                     if(typeof(renderer) !== 'undefined'){
4366                         value = renderer(d.data[cm.getDataIndex(i)], false, d);
4367                     }
4368                     
4369                     if(typeof(value) === 'object'){
4370                         renders.push({
4371                             id : id,
4372                             cfg : value 
4373                         })
4374                     }
4375                     
4376                     var td = {
4377                         tag: 'td',
4378                         id: id,
4379                         html: (typeof(value) === 'object') ? '' : value
4380                     };
4381                     
4382                     if(typeof(config.width) != 'undefined'){
4383                         td.style = 'width:' +  config.width + 'px';
4384                     }
4385                     
4386                     row.cn.push(td);
4387                    
4388                 }
4389                 
4390                 tbody.createChild(row);
4391                 
4392             });
4393         }
4394         
4395         
4396         if(renders.length){
4397             var _this = this;
4398             Roo.each(renders, function(r){
4399                 _this.renderColumn(r);
4400             })
4401         }
4402
4403         if(this.loadMask){
4404             this.maskEl.hide();
4405         }
4406     },
4407     
4408     onBeforeLoad : function()
4409     {
4410         Roo.log('ds onBeforeLoad');
4411         
4412         this.clear();
4413         
4414         if(this.loadMask){
4415             this.maskEl.show();
4416         }
4417     },
4418     
4419     clear : function()
4420     {
4421         this.el.select('tbody', true).first().dom.innerHTML = '';
4422     },
4423     
4424     getSelectionModel : function(){
4425         if(!this.selModel){
4426             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
4427         }
4428         return this.selModel;
4429     },
4430     
4431     renderColumn : function(r)
4432     {
4433         var _this = this;
4434         r.cfg.render(Roo.get(r.id));
4435         
4436         if(r.cfg.cn){
4437             Roo.each(r.cfg.cn, function(c){
4438                 var child = {
4439                     id: r.id,
4440                     cfg: c
4441                 }
4442                 _this.renderColumn(child);
4443             })
4444         }
4445     }
4446    
4447 });
4448
4449  
4450
4451  /*
4452  * - LGPL
4453  *
4454  * table cell
4455  * 
4456  */
4457
4458 /**
4459  * @class Roo.bootstrap.TableCell
4460  * @extends Roo.bootstrap.Component
4461  * Bootstrap TableCell class
4462  * @cfg {String} html cell contain text
4463  * @cfg {String} cls cell class
4464  * @cfg {String} tag cell tag (td|th) default td
4465  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
4466  * @cfg {String} align Aligns the content in a cell
4467  * @cfg {String} axis Categorizes cells
4468  * @cfg {String} bgcolor Specifies the background color of a cell
4469  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
4470  * @cfg {Number} colspan Specifies the number of columns a cell should span
4471  * @cfg {String} headers Specifies one or more header cells a cell is related to
4472  * @cfg {Number} height Sets the height of a cell
4473  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
4474  * @cfg {Number} rowspan Sets the number of rows a cell should span
4475  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
4476  * @cfg {String} valign Vertical aligns the content in a cell
4477  * @cfg {Number} width Specifies the width of a cell
4478  * 
4479  * @constructor
4480  * Create a new TableCell
4481  * @param {Object} config The config object
4482  */
4483
4484 Roo.bootstrap.TableCell = function(config){
4485     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
4486 };
4487
4488 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
4489     
4490     html: false,
4491     cls: false,
4492     tag: false,
4493     abbr: false,
4494     align: false,
4495     axis: false,
4496     bgcolor: false,
4497     charoff: false,
4498     colspan: false,
4499     headers: false,
4500     height: false,
4501     nowrap: false,
4502     rowspan: false,
4503     scope: false,
4504     valign: false,
4505     width: false,
4506     
4507     
4508     getAutoCreate : function(){
4509         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
4510         
4511         cfg = {
4512             tag: 'td'
4513         }
4514         
4515         if(this.tag){
4516             cfg.tag = this.tag;
4517         }
4518         
4519         if (this.html) {
4520             cfg.html=this.html
4521         }
4522         if (this.cls) {
4523             cfg.cls=this.cls
4524         }
4525         if (this.abbr) {
4526             cfg.abbr=this.abbr
4527         }
4528         if (this.align) {
4529             cfg.align=this.align
4530         }
4531         if (this.axis) {
4532             cfg.axis=this.axis
4533         }
4534         if (this.bgcolor) {
4535             cfg.bgcolor=this.bgcolor
4536         }
4537         if (this.charoff) {
4538             cfg.charoff=this.charoff
4539         }
4540         if (this.colspan) {
4541             cfg.colspan=this.colspan
4542         }
4543         if (this.headers) {
4544             cfg.headers=this.headers
4545         }
4546         if (this.height) {
4547             cfg.height=this.height
4548         }
4549         if (this.nowrap) {
4550             cfg.nowrap=this.nowrap
4551         }
4552         if (this.rowspan) {
4553             cfg.rowspan=this.rowspan
4554         }
4555         if (this.scope) {
4556             cfg.scope=this.scope
4557         }
4558         if (this.valign) {
4559             cfg.valign=this.valign
4560         }
4561         if (this.width) {
4562             cfg.width=this.width
4563         }
4564         
4565         
4566         return cfg;
4567     }
4568    
4569 });
4570
4571  
4572
4573  /*
4574  * - LGPL
4575  *
4576  * table row
4577  * 
4578  */
4579
4580 /**
4581  * @class Roo.bootstrap.TableRow
4582  * @extends Roo.bootstrap.Component
4583  * Bootstrap TableRow class
4584  * @cfg {String} cls row class
4585  * @cfg {String} align Aligns the content in a table row
4586  * @cfg {String} bgcolor Specifies a background color for a table row
4587  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
4588  * @cfg {String} valign Vertical aligns the content in a table row
4589  * 
4590  * @constructor
4591  * Create a new TableRow
4592  * @param {Object} config The config object
4593  */
4594
4595 Roo.bootstrap.TableRow = function(config){
4596     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
4597 };
4598
4599 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
4600     
4601     cls: false,
4602     align: false,
4603     bgcolor: false,
4604     charoff: false,
4605     valign: false,
4606     
4607     getAutoCreate : function(){
4608         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
4609         
4610         cfg = {
4611             tag: 'tr'
4612         }
4613             
4614         if(this.cls){
4615             cfg.cls = this.cls;
4616         }
4617         if(this.align){
4618             cfg.align = this.align;
4619         }
4620         if(this.bgcolor){
4621             cfg.bgcolor = this.bgcolor;
4622         }
4623         if(this.charoff){
4624             cfg.charoff = this.charoff;
4625         }
4626         if(this.valign){
4627             cfg.valign = this.valign;
4628         }
4629         
4630         return cfg;
4631     }
4632    
4633 });
4634
4635  
4636
4637  /*
4638  * - LGPL
4639  *
4640  * table body
4641  * 
4642  */
4643
4644 /**
4645  * @class Roo.bootstrap.TableBody
4646  * @extends Roo.bootstrap.Component
4647  * Bootstrap TableBody class
4648  * @cfg {String} cls element class
4649  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
4650  * @cfg {String} align Aligns the content inside the element
4651  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
4652  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
4653  * 
4654  * @constructor
4655  * Create a new TableBody
4656  * @param {Object} config The config object
4657  */
4658
4659 Roo.bootstrap.TableBody = function(config){
4660     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
4661 };
4662
4663 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
4664     
4665     cls: false,
4666     tag: false,
4667     align: false,
4668     charoff: false,
4669     valign: false,
4670     
4671     getAutoCreate : function(){
4672         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
4673         
4674         cfg = {
4675             tag: 'tbody'
4676         }
4677             
4678         if (this.cls) {
4679             cfg.cls=this.cls
4680         }
4681         if(this.tag){
4682             cfg.tag = this.tag;
4683         }
4684         
4685         if(this.align){
4686             cfg.align = this.align;
4687         }
4688         if(this.charoff){
4689             cfg.charoff = this.charoff;
4690         }
4691         if(this.valign){
4692             cfg.valign = this.valign;
4693         }
4694         
4695         return cfg;
4696     }
4697     
4698     
4699 //    initEvents : function()
4700 //    {
4701 //        
4702 //        if(!this.store){
4703 //            return;
4704 //        }
4705 //        
4706 //        this.store = Roo.factory(this.store, Roo.data);
4707 //        this.store.on('load', this.onLoad, this);
4708 //        
4709 //        this.store.load();
4710 //        
4711 //    },
4712 //    
4713 //    onLoad: function () 
4714 //    {   
4715 //        this.fireEvent('load', this);
4716 //    }
4717 //    
4718 //   
4719 });
4720
4721  
4722
4723  /*
4724  * Based on:
4725  * Ext JS Library 1.1.1
4726  * Copyright(c) 2006-2007, Ext JS, LLC.
4727  *
4728  * Originally Released Under LGPL - original licence link has changed is not relivant.
4729  *
4730  * Fork - LGPL
4731  * <script type="text/javascript">
4732  */
4733
4734 // as we use this in bootstrap.
4735 Roo.namespace('Roo.form');
4736  /**
4737  * @class Roo.form.Action
4738  * Internal Class used to handle form actions
4739  * @constructor
4740  * @param {Roo.form.BasicForm} el The form element or its id
4741  * @param {Object} config Configuration options
4742  */
4743
4744  
4745  
4746 // define the action interface
4747 Roo.form.Action = function(form, options){
4748     this.form = form;
4749     this.options = options || {};
4750 };
4751 /**
4752  * Client Validation Failed
4753  * @const 
4754  */
4755 Roo.form.Action.CLIENT_INVALID = 'client';
4756 /**
4757  * Server Validation Failed
4758  * @const 
4759  */
4760 Roo.form.Action.SERVER_INVALID = 'server';
4761  /**
4762  * Connect to Server Failed
4763  * @const 
4764  */
4765 Roo.form.Action.CONNECT_FAILURE = 'connect';
4766 /**
4767  * Reading Data from Server Failed
4768  * @const 
4769  */
4770 Roo.form.Action.LOAD_FAILURE = 'load';
4771
4772 Roo.form.Action.prototype = {
4773     type : 'default',
4774     failureType : undefined,
4775     response : undefined,
4776     result : undefined,
4777
4778     // interface method
4779     run : function(options){
4780
4781     },
4782
4783     // interface method
4784     success : function(response){
4785
4786     },
4787
4788     // interface method
4789     handleResponse : function(response){
4790
4791     },
4792
4793     // default connection failure
4794     failure : function(response){
4795         
4796         this.response = response;
4797         this.failureType = Roo.form.Action.CONNECT_FAILURE;
4798         this.form.afterAction(this, false);
4799     },
4800
4801     processResponse : function(response){
4802         this.response = response;
4803         if(!response.responseText){
4804             return true;
4805         }
4806         this.result = this.handleResponse(response);
4807         return this.result;
4808     },
4809
4810     // utility functions used internally
4811     getUrl : function(appendParams){
4812         var url = this.options.url || this.form.url || this.form.el.dom.action;
4813         if(appendParams){
4814             var p = this.getParams();
4815             if(p){
4816                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
4817             }
4818         }
4819         return url;
4820     },
4821
4822     getMethod : function(){
4823         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
4824     },
4825
4826     getParams : function(){
4827         var bp = this.form.baseParams;
4828         var p = this.options.params;
4829         if(p){
4830             if(typeof p == "object"){
4831                 p = Roo.urlEncode(Roo.applyIf(p, bp));
4832             }else if(typeof p == 'string' && bp){
4833                 p += '&' + Roo.urlEncode(bp);
4834             }
4835         }else if(bp){
4836             p = Roo.urlEncode(bp);
4837         }
4838         return p;
4839     },
4840
4841     createCallback : function(){
4842         return {
4843             success: this.success,
4844             failure: this.failure,
4845             scope: this,
4846             timeout: (this.form.timeout*1000),
4847             upload: this.form.fileUpload ? this.success : undefined
4848         };
4849     }
4850 };
4851
4852 Roo.form.Action.Submit = function(form, options){
4853     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
4854 };
4855
4856 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
4857     type : 'submit',
4858
4859     haveProgress : false,
4860     uploadComplete : false,
4861     
4862     // uploadProgress indicator.
4863     uploadProgress : function()
4864     {
4865         if (!this.form.progressUrl) {
4866             return;
4867         }
4868         
4869         if (!this.haveProgress) {
4870             Roo.MessageBox.progress("Uploading", "Uploading");
4871         }
4872         if (this.uploadComplete) {
4873            Roo.MessageBox.hide();
4874            return;
4875         }
4876         
4877         this.haveProgress = true;
4878    
4879         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
4880         
4881         var c = new Roo.data.Connection();
4882         c.request({
4883             url : this.form.progressUrl,
4884             params: {
4885                 id : uid
4886             },
4887             method: 'GET',
4888             success : function(req){
4889                //console.log(data);
4890                 var rdata = false;
4891                 var edata;
4892                 try  {
4893                    rdata = Roo.decode(req.responseText)
4894                 } catch (e) {
4895                     Roo.log("Invalid data from server..");
4896                     Roo.log(edata);
4897                     return;
4898                 }
4899                 if (!rdata || !rdata.success) {
4900                     Roo.log(rdata);
4901                     Roo.MessageBox.alert(Roo.encode(rdata));
4902                     return;
4903                 }
4904                 var data = rdata.data;
4905                 
4906                 if (this.uploadComplete) {
4907                    Roo.MessageBox.hide();
4908                    return;
4909                 }
4910                    
4911                 if (data){
4912                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
4913                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
4914                     );
4915                 }
4916                 this.uploadProgress.defer(2000,this);
4917             },
4918        
4919             failure: function(data) {
4920                 Roo.log('progress url failed ');
4921                 Roo.log(data);
4922             },
4923             scope : this
4924         });
4925            
4926     },
4927     
4928     
4929     run : function()
4930     {
4931         // run get Values on the form, so it syncs any secondary forms.
4932         this.form.getValues();
4933         
4934         var o = this.options;
4935         var method = this.getMethod();
4936         var isPost = method == 'POST';
4937         if(o.clientValidation === false || this.form.isValid()){
4938             
4939             if (this.form.progressUrl) {
4940                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
4941                     (new Date() * 1) + '' + Math.random());
4942                     
4943             } 
4944             
4945             
4946             Roo.Ajax.request(Roo.apply(this.createCallback(), {
4947                 form:this.form.el.dom,
4948                 url:this.getUrl(!isPost),
4949                 method: method,
4950                 params:isPost ? this.getParams() : null,
4951                 isUpload: this.form.fileUpload
4952             }));
4953             
4954             this.uploadProgress();
4955
4956         }else if (o.clientValidation !== false){ // client validation failed
4957             this.failureType = Roo.form.Action.CLIENT_INVALID;
4958             this.form.afterAction(this, false);
4959         }
4960     },
4961
4962     success : function(response)
4963     {
4964         this.uploadComplete= true;
4965         if (this.haveProgress) {
4966             Roo.MessageBox.hide();
4967         }
4968         
4969         
4970         var result = this.processResponse(response);
4971         if(result === true || result.success){
4972             this.form.afterAction(this, true);
4973             return;
4974         }
4975         if(result.errors){
4976             this.form.markInvalid(result.errors);
4977             this.failureType = Roo.form.Action.SERVER_INVALID;
4978         }
4979         this.form.afterAction(this, false);
4980     },
4981     failure : function(response)
4982     {
4983         this.uploadComplete= true;
4984         if (this.haveProgress) {
4985             Roo.MessageBox.hide();
4986         }
4987         
4988         this.response = response;
4989         this.failureType = Roo.form.Action.CONNECT_FAILURE;
4990         this.form.afterAction(this, false);
4991     },
4992     
4993     handleResponse : function(response){
4994         if(this.form.errorReader){
4995             var rs = this.form.errorReader.read(response);
4996             var errors = [];
4997             if(rs.records){
4998                 for(var i = 0, len = rs.records.length; i < len; i++) {
4999                     var r = rs.records[i];
5000                     errors[i] = r.data;
5001                 }
5002             }
5003             if(errors.length < 1){
5004                 errors = null;
5005             }
5006             return {
5007                 success : rs.success,
5008                 errors : errors
5009             };
5010         }
5011         var ret = false;
5012         try {
5013             ret = Roo.decode(response.responseText);
5014         } catch (e) {
5015             ret = {
5016                 success: false,
5017                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
5018                 errors : []
5019             };
5020         }
5021         return ret;
5022         
5023     }
5024 });
5025
5026
5027 Roo.form.Action.Load = function(form, options){
5028     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
5029     this.reader = this.form.reader;
5030 };
5031
5032 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
5033     type : 'load',
5034
5035     run : function(){
5036         
5037         Roo.Ajax.request(Roo.apply(
5038                 this.createCallback(), {
5039                     method:this.getMethod(),
5040                     url:this.getUrl(false),
5041                     params:this.getParams()
5042         }));
5043     },
5044
5045     success : function(response){
5046         
5047         var result = this.processResponse(response);
5048         if(result === true || !result.success || !result.data){
5049             this.failureType = Roo.form.Action.LOAD_FAILURE;
5050             this.form.afterAction(this, false);
5051             return;
5052         }
5053         this.form.clearInvalid();
5054         this.form.setValues(result.data);
5055         this.form.afterAction(this, true);
5056     },
5057
5058     handleResponse : function(response){
5059         if(this.form.reader){
5060             var rs = this.form.reader.read(response);
5061             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
5062             return {
5063                 success : rs.success,
5064                 data : data
5065             };
5066         }
5067         return Roo.decode(response.responseText);
5068     }
5069 });
5070
5071 Roo.form.Action.ACTION_TYPES = {
5072     'load' : Roo.form.Action.Load,
5073     'submit' : Roo.form.Action.Submit
5074 };/*
5075  * - LGPL
5076  *
5077  * form
5078  * 
5079  */
5080
5081 /**
5082  * @class Roo.bootstrap.Form
5083  * @extends Roo.bootstrap.Component
5084  * Bootstrap Form class
5085  * @cfg {String} method  GET | POST (default POST)
5086  * @cfg {String} labelAlign top | left (default top)
5087   * @cfg {String} align left  | right - for navbars
5088
5089  * 
5090  * @constructor
5091  * Create a new Form
5092  * @param {Object} config The config object
5093  */
5094
5095
5096 Roo.bootstrap.Form = function(config){
5097     Roo.bootstrap.Form.superclass.constructor.call(this, config);
5098     this.addEvents({
5099         /**
5100          * @event clientvalidation
5101          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
5102          * @param {Form} this
5103          * @param {Boolean} valid true if the form has passed client-side validation
5104          */
5105         clientvalidation: true,
5106         /**
5107          * @event beforeaction
5108          * Fires before any action is performed. Return false to cancel the action.
5109          * @param {Form} this
5110          * @param {Action} action The action to be performed
5111          */
5112         beforeaction: true,
5113         /**
5114          * @event actionfailed
5115          * Fires when an action fails.
5116          * @param {Form} this
5117          * @param {Action} action The action that failed
5118          */
5119         actionfailed : true,
5120         /**
5121          * @event actioncomplete
5122          * Fires when an action is completed.
5123          * @param {Form} this
5124          * @param {Action} action The action that completed
5125          */
5126         actioncomplete : true
5127     });
5128     
5129 };
5130
5131 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
5132       
5133      /**
5134      * @cfg {String} method
5135      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
5136      */
5137     method : 'POST',
5138     /**
5139      * @cfg {String} url
5140      * The URL to use for form actions if one isn't supplied in the action options.
5141      */
5142     /**
5143      * @cfg {Boolean} fileUpload
5144      * Set to true if this form is a file upload.
5145      */
5146      
5147     /**
5148      * @cfg {Object} baseParams
5149      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
5150      */
5151       
5152     /**
5153      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
5154      */
5155     timeout: 30,
5156     /**
5157      * @cfg {Sting} align (left|right) for navbar forms
5158      */
5159     align : 'left',
5160
5161     // private
5162     activeAction : null,
5163  
5164     /**
5165      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
5166      * element by passing it or its id or mask the form itself by passing in true.
5167      * @type Mixed
5168      */
5169     waitMsgTarget : false,
5170     
5171      
5172     
5173     /**
5174      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
5175      * element by passing it or its id or mask the form itself by passing in true.
5176      * @type Mixed
5177      */
5178     
5179     getAutoCreate : function(){
5180         
5181         var cfg = {
5182             tag: 'form',
5183             method : this.method || 'POST',
5184             id : this.id || Roo.id(),
5185             cls : ''
5186         }
5187         if (this.parent().xtype.match(/^Nav/)) {
5188             cfg.cls = 'navbar-form navbar-' + this.align;
5189             
5190         }
5191         
5192         if (this.labelAlign == 'left' ) {
5193             cfg.cls += ' form-horizontal';
5194         }
5195         
5196         
5197         return cfg;
5198     },
5199     initEvents : function()
5200     {
5201         this.el.on('submit', this.onSubmit, this);
5202         
5203         
5204     },
5205     // private
5206     onSubmit : function(e){
5207         e.stopEvent();
5208     },
5209     
5210      /**
5211      * Returns true if client-side validation on the form is successful.
5212      * @return Boolean
5213      */
5214     isValid : function(){
5215         var items = this.getItems();
5216         var valid = true;
5217         items.each(function(f){
5218            if(!f.validate()){
5219                valid = false;
5220                
5221            }
5222         });
5223         return valid;
5224     },
5225     /**
5226      * Returns true if any fields in this form have changed since their original load.
5227      * @return Boolean
5228      */
5229     isDirty : function(){
5230         var dirty = false;
5231         var items = this.getItems();
5232         items.each(function(f){
5233            if(f.isDirty()){
5234                dirty = true;
5235                return false;
5236            }
5237            return true;
5238         });
5239         return dirty;
5240     },
5241      /**
5242      * Performs a predefined action (submit or load) or custom actions you define on this form.
5243      * @param {String} actionName The name of the action type
5244      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
5245      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
5246      * accept other config options):
5247      * <pre>
5248 Property          Type             Description
5249 ----------------  ---------------  ----------------------------------------------------------------------------------
5250 url               String           The url for the action (defaults to the form's url)
5251 method            String           The form method to use (defaults to the form's method, or POST if not defined)
5252 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
5253 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
5254                                    validate the form on the client (defaults to false)
5255      * </pre>
5256      * @return {BasicForm} this
5257      */
5258     doAction : function(action, options){
5259         if(typeof action == 'string'){
5260             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
5261         }
5262         if(this.fireEvent('beforeaction', this, action) !== false){
5263             this.beforeAction(action);
5264             action.run.defer(100, action);
5265         }
5266         return this;
5267     },
5268     
5269     // private
5270     beforeAction : function(action){
5271         var o = action.options;
5272         
5273         // not really supported yet.. ??
5274         
5275         //if(this.waitMsgTarget === true){
5276             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
5277         //}else if(this.waitMsgTarget){
5278         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
5279         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
5280         //}else {
5281         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
5282        // }
5283          
5284     },
5285
5286     // private
5287     afterAction : function(action, success){
5288         this.activeAction = null;
5289         var o = action.options;
5290         
5291         //if(this.waitMsgTarget === true){
5292             this.el.unmask();
5293         //}else if(this.waitMsgTarget){
5294         //    this.waitMsgTarget.unmask();
5295         //}else{
5296         //    Roo.MessageBox.updateProgress(1);
5297         //    Roo.MessageBox.hide();
5298        // }
5299         // 
5300         if(success){
5301             if(o.reset){
5302                 this.reset();
5303             }
5304             Roo.callback(o.success, o.scope, [this, action]);
5305             this.fireEvent('actioncomplete', this, action);
5306             
5307         }else{
5308             
5309             // failure condition..
5310             // we have a scenario where updates need confirming.
5311             // eg. if a locking scenario exists..
5312             // we look for { errors : { needs_confirm : true }} in the response.
5313             if (
5314                 (typeof(action.result) != 'undefined')  &&
5315                 (typeof(action.result.errors) != 'undefined')  &&
5316                 (typeof(action.result.errors.needs_confirm) != 'undefined')
5317            ){
5318                 var _t = this;
5319                 Roo.log("not supported yet");
5320                  /*
5321                 
5322                 Roo.MessageBox.confirm(
5323                     "Change requires confirmation",
5324                     action.result.errorMsg,
5325                     function(r) {
5326                         if (r != 'yes') {
5327                             return;
5328                         }
5329                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
5330                     }
5331                     
5332                 );
5333                 */
5334                 
5335                 
5336                 return;
5337             }
5338             
5339             Roo.callback(o.failure, o.scope, [this, action]);
5340             // show an error message if no failed handler is set..
5341             if (!this.hasListener('actionfailed')) {
5342                 Roo.log("need to add dialog support");
5343                 /*
5344                 Roo.MessageBox.alert("Error",
5345                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
5346                         action.result.errorMsg :
5347                         "Saving Failed, please check your entries or try again"
5348                 );
5349                 */
5350             }
5351             
5352             this.fireEvent('actionfailed', this, action);
5353         }
5354         
5355     },
5356     /**
5357      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
5358      * @param {String} id The value to search for
5359      * @return Field
5360      */
5361     findField : function(id){
5362         var items = this.getItems();
5363         var field = items.get(id);
5364         if(!field){
5365              items.each(function(f){
5366                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
5367                     field = f;
5368                     return false;
5369                 }
5370                 return true;
5371             });
5372         }
5373         return field || null;
5374     },
5375      /**
5376      * Mark fields in this form invalid in bulk.
5377      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
5378      * @return {BasicForm} this
5379      */
5380     markInvalid : function(errors){
5381         if(errors instanceof Array){
5382             for(var i = 0, len = errors.length; i < len; i++){
5383                 var fieldError = errors[i];
5384                 var f = this.findField(fieldError.id);
5385                 if(f){
5386                     f.markInvalid(fieldError.msg);
5387                 }
5388             }
5389         }else{
5390             var field, id;
5391             for(id in errors){
5392                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
5393                     field.markInvalid(errors[id]);
5394                 }
5395             }
5396         }
5397         //Roo.each(this.childForms || [], function (f) {
5398         //    f.markInvalid(errors);
5399         //});
5400         
5401         return this;
5402     },
5403
5404     /**
5405      * Set values for fields in this form in bulk.
5406      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
5407      * @return {BasicForm} this
5408      */
5409     setValues : function(values){
5410         if(values instanceof Array){ // array of objects
5411             for(var i = 0, len = values.length; i < len; i++){
5412                 var v = values[i];
5413                 var f = this.findField(v.id);
5414                 if(f){
5415                     f.setValue(v.value);
5416                     if(this.trackResetOnLoad){
5417                         f.originalValue = f.getValue();
5418                     }
5419                 }
5420             }
5421         }else{ // object hash
5422             var field, id;
5423             for(id in values){
5424                 if(typeof values[id] != 'function' && (field = this.findField(id))){
5425                     
5426                     if (field.setFromData && 
5427                         field.valueField && 
5428                         field.displayField &&
5429                         // combos' with local stores can 
5430                         // be queried via setValue()
5431                         // to set their value..
5432                         (field.store && !field.store.isLocal)
5433                         ) {
5434                         // it's a combo
5435                         var sd = { };
5436                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
5437                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
5438                         field.setFromData(sd);
5439                         
5440                     } else {
5441                         field.setValue(values[id]);
5442                     }
5443                     
5444                     
5445                     if(this.trackResetOnLoad){
5446                         field.originalValue = field.getValue();
5447                     }
5448                 }
5449             }
5450         }
5451          
5452         //Roo.each(this.childForms || [], function (f) {
5453         //    f.setValues(values);
5454         //});
5455                 
5456         return this;
5457     },
5458
5459     /**
5460      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
5461      * they are returned as an array.
5462      * @param {Boolean} asString
5463      * @return {Object}
5464      */
5465     getValues : function(asString){
5466         //if (this.childForms) {
5467             // copy values from the child forms
5468         //    Roo.each(this.childForms, function (f) {
5469         //        this.setValues(f.getValues());
5470         //    }, this);
5471         //}
5472         
5473         
5474         
5475         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
5476         if(asString === true){
5477             return fs;
5478         }
5479         return Roo.urlDecode(fs);
5480     },
5481     
5482     /**
5483      * Returns the fields in this form as an object with key/value pairs. 
5484      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
5485      * @return {Object}
5486      */
5487     getFieldValues : function(with_hidden)
5488     {
5489         var items = this.getItems();
5490         var ret = {};
5491         items.each(function(f){
5492             if (!f.getName()) {
5493                 return;
5494             }
5495             var v = f.getValue();
5496             if (f.inputType =='radio') {
5497                 if (typeof(ret[f.getName()]) == 'undefined') {
5498                     ret[f.getName()] = ''; // empty..
5499                 }
5500                 
5501                 if (!f.el.dom.checked) {
5502                     return;
5503                     
5504                 }
5505                 v = f.el.dom.value;
5506                 
5507             }
5508             
5509             // not sure if this supported any more..
5510             if ((typeof(v) == 'object') && f.getRawValue) {
5511                 v = f.getRawValue() ; // dates..
5512             }
5513             // combo boxes where name != hiddenName...
5514             if (f.name != f.getName()) {
5515                 ret[f.name] = f.getRawValue();
5516             }
5517             ret[f.getName()] = v;
5518         });
5519         
5520         return ret;
5521     },
5522
5523     /**
5524      * Clears all invalid messages in this form.
5525      * @return {BasicForm} this
5526      */
5527     clearInvalid : function(){
5528         var items = this.getItems();
5529         
5530         items.each(function(f){
5531            f.clearInvalid();
5532         });
5533         
5534         
5535         
5536         return this;
5537     },
5538
5539     /**
5540      * Resets this form.
5541      * @return {BasicForm} this
5542      */
5543     reset : function(){
5544         var items = this.getItems();
5545         items.each(function(f){
5546             f.reset();
5547         });
5548         
5549         Roo.each(this.childForms || [], function (f) {
5550             f.reset();
5551         });
5552        
5553         
5554         return this;
5555     },
5556     getItems : function()
5557     {
5558         var r=new Roo.util.MixedCollection(false, function(o){
5559             return o.id || (o.id = Roo.id());
5560         });
5561         var iter = function(el) {
5562             if (el.inputEl) {
5563                 r.add(el);
5564             }
5565             if (!el.items) {
5566                 return;
5567             }
5568             Roo.each(el.items,function(e) {
5569                 iter(e);
5570             });
5571             
5572             
5573         };
5574         iter(this);
5575         return r;
5576         
5577         
5578         
5579         
5580     }
5581     
5582 });
5583
5584  
5585 /*
5586  * Based on:
5587  * Ext JS Library 1.1.1
5588  * Copyright(c) 2006-2007, Ext JS, LLC.
5589  *
5590  * Originally Released Under LGPL - original licence link has changed is not relivant.
5591  *
5592  * Fork - LGPL
5593  * <script type="text/javascript">
5594  */
5595 /**
5596  * @class Roo.form.VTypes
5597  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
5598  * @singleton
5599  */
5600 Roo.form.VTypes = function(){
5601     // closure these in so they are only created once.
5602     var alpha = /^[a-zA-Z_]+$/;
5603     var alphanum = /^[a-zA-Z0-9_]+$/;
5604     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
5605     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
5606
5607     // All these messages and functions are configurable
5608     return {
5609         /**
5610          * The function used to validate email addresses
5611          * @param {String} value The email address
5612          */
5613         'email' : function(v){
5614             return email.test(v);
5615         },
5616         /**
5617          * The error text to display when the email validation function returns false
5618          * @type String
5619          */
5620         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
5621         /**
5622          * The keystroke filter mask to be applied on email input
5623          * @type RegExp
5624          */
5625         'emailMask' : /[a-z0-9_\.\-@]/i,
5626
5627         /**
5628          * The function used to validate URLs
5629          * @param {String} value The URL
5630          */
5631         'url' : function(v){
5632             return url.test(v);
5633         },
5634         /**
5635          * The error text to display when the url validation function returns false
5636          * @type String
5637          */
5638         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
5639         
5640         /**
5641          * The function used to validate alpha values
5642          * @param {String} value The value
5643          */
5644         'alpha' : function(v){
5645             return alpha.test(v);
5646         },
5647         /**
5648          * The error text to display when the alpha validation function returns false
5649          * @type String
5650          */
5651         'alphaText' : 'This field should only contain letters and _',
5652         /**
5653          * The keystroke filter mask to be applied on alpha input
5654          * @type RegExp
5655          */
5656         'alphaMask' : /[a-z_]/i,
5657
5658         /**
5659          * The function used to validate alphanumeric values
5660          * @param {String} value The value
5661          */
5662         'alphanum' : function(v){
5663             return alphanum.test(v);
5664         },
5665         /**
5666          * The error text to display when the alphanumeric validation function returns false
5667          * @type String
5668          */
5669         'alphanumText' : 'This field should only contain letters, numbers and _',
5670         /**
5671          * The keystroke filter mask to be applied on alphanumeric input
5672          * @type RegExp
5673          */
5674         'alphanumMask' : /[a-z0-9_]/i
5675     };
5676 }();/*
5677  * - LGPL
5678  *
5679  * Input
5680  * 
5681  */
5682
5683 /**
5684  * @class Roo.bootstrap.Input
5685  * @extends Roo.bootstrap.Component
5686  * Bootstrap Input class
5687  * @cfg {Boolean} disabled is it disabled
5688  * @cfg {String} fieldLabel - the label associated
5689  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
5690  * @cfg {String} name name of the input
5691  * @cfg {string} fieldLabel - the label associated
5692  * @cfg {string}  inputType - input / file submit ...
5693  * @cfg {string} placeholder - placeholder to put in text.
5694  * @cfg {string}  before - input group add on before
5695  * @cfg {string} after - input group add on after
5696  * @cfg {string} size - (lg|sm) or leave empty..
5697  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
5698  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
5699  * @cfg {Number} md colspan out of 12 for computer-sized screens
5700  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
5701  * @cfg {string} value default value of the input
5702  * @cfg {Number} labelWidth set the width of label (0-12)
5703  * @cfg {String} labelAlign (top|left)
5704  * @cfg {Boolean} readOnly Specifies that the field should be read-only
5705  * 
5706  * 
5707  * @constructor
5708  * Create a new Input
5709  * @param {Object} config The config object
5710  */
5711
5712 Roo.bootstrap.Input = function(config){
5713     Roo.bootstrap.Input.superclass.constructor.call(this, config);
5714    
5715         this.addEvents({
5716             /**
5717              * @event focus
5718              * Fires when this field receives input focus.
5719              * @param {Roo.form.Field} this
5720              */
5721             focus : true,
5722             /**
5723              * @event blur
5724              * Fires when this field loses input focus.
5725              * @param {Roo.form.Field} this
5726              */
5727             blur : true,
5728             /**
5729              * @event specialkey
5730              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
5731              * {@link Roo.EventObject#getKey} to determine which key was pressed.
5732              * @param {Roo.form.Field} this
5733              * @param {Roo.EventObject} e The event object
5734              */
5735             specialkey : true,
5736             /**
5737              * @event change
5738              * Fires just before the field blurs if the field value has changed.
5739              * @param {Roo.form.Field} this
5740              * @param {Mixed} newValue The new value
5741              * @param {Mixed} oldValue The original value
5742              */
5743             change : true,
5744             /**
5745              * @event invalid
5746              * Fires after the field has been marked as invalid.
5747              * @param {Roo.form.Field} this
5748              * @param {String} msg The validation message
5749              */
5750             invalid : true,
5751             /**
5752              * @event valid
5753              * Fires after the field has been validated with no errors.
5754              * @param {Roo.form.Field} this
5755              */
5756             valid : true,
5757              /**
5758              * @event keyup
5759              * Fires after the key up
5760              * @param {Roo.form.Field} this
5761              * @param {Roo.EventObject}  e The event Object
5762              */
5763             keyup : true
5764         });
5765 };
5766
5767 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
5768      /**
5769      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
5770       automatic validation (defaults to "keyup").
5771      */
5772     validationEvent : "keyup",
5773      /**
5774      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
5775      */
5776     validateOnBlur : true,
5777     /**
5778      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
5779      */
5780     validationDelay : 250,
5781      /**
5782      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
5783      */
5784     focusClass : "x-form-focus",  // not needed???
5785     
5786        
5787     /**
5788      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
5789      */
5790     invalidClass : "has-error",
5791     
5792     /**
5793      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
5794      */
5795     selectOnFocus : false,
5796     
5797      /**
5798      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
5799      */
5800     maskRe : null,
5801        /**
5802      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
5803      */
5804     vtype : null,
5805     
5806       /**
5807      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
5808      */
5809     disableKeyFilter : false,
5810     
5811        /**
5812      * @cfg {Boolean} disabled True to disable the field (defaults to false).
5813      */
5814     disabled : false,
5815      /**
5816      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
5817      */
5818     allowBlank : true,
5819     /**
5820      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
5821      */
5822     blankText : "This field is required",
5823     
5824      /**
5825      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
5826      */
5827     minLength : 0,
5828     /**
5829      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
5830      */
5831     maxLength : Number.MAX_VALUE,
5832     /**
5833      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
5834      */
5835     minLengthText : "The minimum length for this field is {0}",
5836     /**
5837      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
5838      */
5839     maxLengthText : "The maximum length for this field is {0}",
5840   
5841     
5842     /**
5843      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
5844      * If available, this function will be called only after the basic validators all return true, and will be passed the
5845      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
5846      */
5847     validator : null,
5848     /**
5849      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
5850      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
5851      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
5852      */
5853     regex : null,
5854     /**
5855      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
5856      */
5857     regexText : "",
5858     
5859     
5860     
5861     fieldLabel : '',
5862     inputType : 'text',
5863     
5864     name : false,
5865     placeholder: false,
5866     before : false,
5867     after : false,
5868     size : false,
5869     // private
5870     hasFocus : false,
5871     preventMark: false,
5872     isFormField : true,
5873     value : '',
5874     labelWidth : 2,
5875     labelAlign : false,
5876     readOnly : false,
5877     
5878     parentLabelAlign : function()
5879     {
5880         var parent = this;
5881         while (parent.parent()) {
5882             parent = parent.parent();
5883             if (typeof(parent.labelAlign) !='undefined') {
5884                 return parent.labelAlign;
5885             }
5886         }
5887         return 'left';
5888         
5889     },
5890     
5891     getAutoCreate : function(){
5892         
5893         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
5894         
5895         var id = Roo.id();
5896         
5897         var cfg = {};
5898         
5899         if(this.inputType != 'hidden'){
5900             cfg.cls = 'form-group' //input-group
5901         }
5902         
5903         var input =  {
5904             tag: 'input',
5905             id : id,
5906             type : this.inputType,
5907             value : this.value,
5908             cls : 'form-control',
5909             placeholder : this.placeholder || ''
5910             
5911         };
5912         
5913         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
5914             input.maxLength = this.maxLength;
5915         }
5916         
5917         if (this.disabled) {
5918             input.disabled=true;
5919         }
5920         
5921         if (this.readOnly) {
5922             input.readonly=true;
5923         }
5924         
5925         if (this.name) {
5926             input.name = this.name;
5927         }
5928         if (this.size) {
5929             input.cls += ' input-' + this.size;
5930         }
5931         var settings=this;
5932         ['xs','sm','md','lg'].map(function(size){
5933             if (settings[size]) {
5934                 cfg.cls += ' col-' + size + '-' + settings[size];
5935             }
5936         });
5937         
5938         var inputblock = input;
5939         
5940         if (this.before || this.after) {
5941             
5942             inputblock = {
5943                 cls : 'input-group',
5944                 cn :  [] 
5945             };
5946             if (this.before && typeof(this.before) == 'string') {
5947                 
5948                 inputblock.cn.push({
5949                     tag :'span',
5950                     cls : 'roo-input-before input-group-addon',
5951                     html : this.before
5952                 });
5953             }
5954             if (this.before && typeof(this.before) == 'object') {
5955                 this.before = Roo.factory(this.before);
5956                 Roo.log(this.before);
5957                 inputblock.cn.push({
5958                     tag :'span',
5959                     cls : 'roo-input-before input-group-' +
5960                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
5961                 });
5962             }
5963             
5964             inputblock.cn.push(input);
5965             
5966             if (this.after && typeof(this.after) == 'string') {
5967                 inputblock.cn.push({
5968                     tag :'span',
5969                     cls : 'roo-input-after input-group-addon',
5970                     html : this.after
5971                 });
5972             }
5973             if (this.after && typeof(this.after) == 'object') {
5974                 this.after = Roo.factory(this.after);
5975                 Roo.log(this.after);
5976                 inputblock.cn.push({
5977                     tag :'span',
5978                     cls : 'roo-input-after input-group-' +
5979                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
5980                 });
5981             }
5982         };
5983         
5984         if (align ==='left' && this.fieldLabel.length) {
5985                 Roo.log("left and has label");
5986                 cfg.cn = [
5987                     
5988                     {
5989                         tag: 'label',
5990                         'for' :  id,
5991                         cls : 'control-label col-sm-' + this.labelWidth,
5992                         html : this.fieldLabel
5993                         
5994                     },
5995                     {
5996                         cls : "col-sm-" + (12 - this.labelWidth), 
5997                         cn: [
5998                             inputblock
5999                         ]
6000                     }
6001                     
6002                 ];
6003         } else if ( this.fieldLabel.length) {
6004                 Roo.log(" label");
6005                  cfg.cn = [
6006                    
6007                     {
6008                         tag: 'label',
6009                         //cls : 'input-group-addon',
6010                         html : this.fieldLabel
6011                         
6012                     },
6013                     
6014                     inputblock
6015                     
6016                 ];
6017
6018         } else {
6019             
6020                 Roo.log(" no label && no align");
6021                 cfg.cn = [
6022                     
6023                         inputblock
6024                     
6025                 ];
6026                 
6027                 
6028         };
6029         Roo.log('input-parentType: ' + this.parentType);
6030         
6031         if (this.parentType === 'Navbar' &&  this.parent().bar) {
6032            cfg.cls += ' navbar-form';
6033            Roo.log(cfg);
6034         }
6035         
6036         return cfg;
6037         
6038     },
6039     /**
6040      * return the real input element.
6041      */
6042     inputEl: function ()
6043     {
6044         return this.el.select('input.form-control',true).first();
6045     },
6046     setDisabled : function(v)
6047     {
6048         var i  = this.inputEl().dom;
6049         if (!v) {
6050             i.removeAttribute('disabled');
6051             return;
6052             
6053         }
6054         i.setAttribute('disabled','true');
6055     },
6056     initEvents : function()
6057     {
6058         
6059         this.inputEl().on("keydown" , this.fireKey,  this);
6060         this.inputEl().on("focus", this.onFocus,  this);
6061         this.inputEl().on("blur", this.onBlur,  this);
6062         
6063         this.inputEl().relayEvent('keyup', this);
6064
6065         // reference to original value for reset
6066         this.originalValue = this.getValue();
6067         //Roo.form.TextField.superclass.initEvents.call(this);
6068         if(this.validationEvent == 'keyup'){
6069             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
6070             this.inputEl().on('keyup', this.filterValidation, this);
6071         }
6072         else if(this.validationEvent !== false){
6073             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
6074         }
6075         
6076         if(this.selectOnFocus){
6077             this.on("focus", this.preFocus, this);
6078             
6079         }
6080         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
6081             this.inputEl().on("keypress", this.filterKeys, this);
6082         }
6083        /* if(this.grow){
6084             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
6085             this.el.on("click", this.autoSize,  this);
6086         }
6087         */
6088         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
6089             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
6090         }
6091         
6092         if (typeof(this.before) == 'object') {
6093             this.before.render(this.el.select('.roo-input-before',true).first());
6094         }
6095         if (typeof(this.after) == 'object') {
6096             this.after.render(this.el.select('.roo-input-after',true).first());
6097         }
6098         
6099         
6100     },
6101     filterValidation : function(e){
6102         if(!e.isNavKeyPress()){
6103             this.validationTask.delay(this.validationDelay);
6104         }
6105     },
6106      /**
6107      * Validates the field value
6108      * @return {Boolean} True if the value is valid, else false
6109      */
6110     validate : function(){
6111         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
6112         if(this.disabled || this.validateValue(this.getRawValue())){
6113             this.clearInvalid();
6114             return true;
6115         }
6116         return false;
6117     },
6118     
6119     
6120     /**
6121      * Validates a value according to the field's validation rules and marks the field as invalid
6122      * if the validation fails
6123      * @param {Mixed} value The value to validate
6124      * @return {Boolean} True if the value is valid, else false
6125      */
6126     validateValue : function(value){
6127         if(value.length < 1)  { // if it's blank
6128              if(this.allowBlank){
6129                 this.clearInvalid();
6130                 return true;
6131              }else{
6132                 this.markInvalid(this.blankText);
6133                 return false;
6134              }
6135         }
6136         if(value.length < this.minLength){
6137             this.markInvalid(String.format(this.minLengthText, this.minLength));
6138             return false;
6139         }
6140         if(value.length > this.maxLength){
6141             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
6142             return false;
6143         }
6144         if(this.vtype){
6145             var vt = Roo.form.VTypes;
6146             if(!vt[this.vtype](value, this)){
6147                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
6148                 return false;
6149             }
6150         }
6151         if(typeof this.validator == "function"){
6152             var msg = this.validator(value);
6153             if(msg !== true){
6154                 this.markInvalid(msg);
6155                 return false;
6156             }
6157         }
6158         if(this.regex && !this.regex.test(value)){
6159             this.markInvalid(this.regexText);
6160             return false;
6161         }
6162         return true;
6163     },
6164
6165     
6166     
6167      // private
6168     fireKey : function(e){
6169         //Roo.log('field ' + e.getKey());
6170         if(e.isNavKeyPress()){
6171             this.fireEvent("specialkey", this, e);
6172         }
6173     },
6174     focus : function (selectText){
6175         if(this.rendered){
6176             this.inputEl().focus();
6177             if(selectText === true){
6178                 this.inputEl().dom.select();
6179             }
6180         }
6181         return this;
6182     } ,
6183     
6184     onFocus : function(){
6185         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
6186            // this.el.addClass(this.focusClass);
6187         }
6188         if(!this.hasFocus){
6189             this.hasFocus = true;
6190             this.startValue = this.getValue();
6191             this.fireEvent("focus", this);
6192         }
6193     },
6194     
6195     beforeBlur : Roo.emptyFn,
6196
6197     
6198     // private
6199     onBlur : function(){
6200         this.beforeBlur();
6201         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
6202             //this.el.removeClass(this.focusClass);
6203         }
6204         this.hasFocus = false;
6205         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
6206             this.validate();
6207         }
6208         var v = this.getValue();
6209         if(String(v) !== String(this.startValue)){
6210             this.fireEvent('change', this, v, this.startValue);
6211         }
6212         this.fireEvent("blur", this);
6213     },
6214     
6215     /**
6216      * Resets the current field value to the originally loaded value and clears any validation messages
6217      */
6218     reset : function(){
6219         this.setValue(this.originalValue);
6220         this.clearInvalid();
6221     },
6222      /**
6223      * Returns the name of the field
6224      * @return {Mixed} name The name field
6225      */
6226     getName: function(){
6227         return this.name;
6228     },
6229      /**
6230      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
6231      * @return {Mixed} value The field value
6232      */
6233     getValue : function(){
6234         return this.inputEl().getValue();
6235     },
6236     /**
6237      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
6238      * @return {Mixed} value The field value
6239      */
6240     getRawValue : function(){
6241         var v = this.inputEl().getValue();
6242         
6243         return v;
6244     },
6245     
6246     /**
6247      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
6248      * @param {Mixed} value The value to set
6249      */
6250     setRawValue : function(v){
6251         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
6252     },
6253     
6254     selectText : function(start, end){
6255         var v = this.getRawValue();
6256         if(v.length > 0){
6257             start = start === undefined ? 0 : start;
6258             end = end === undefined ? v.length : end;
6259             var d = this.inputEl().dom;
6260             if(d.setSelectionRange){
6261                 d.setSelectionRange(start, end);
6262             }else if(d.createTextRange){
6263                 var range = d.createTextRange();
6264                 range.moveStart("character", start);
6265                 range.moveEnd("character", v.length-end);
6266                 range.select();
6267             }
6268         }
6269     },
6270     
6271     /**
6272      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
6273      * @param {Mixed} value The value to set
6274      */
6275     setValue : function(v){
6276         this.value = v;
6277         if(this.rendered){
6278             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
6279             this.validate();
6280         }
6281     },
6282     
6283     /*
6284     processValue : function(value){
6285         if(this.stripCharsRe){
6286             var newValue = value.replace(this.stripCharsRe, '');
6287             if(newValue !== value){
6288                 this.setRawValue(newValue);
6289                 return newValue;
6290             }
6291         }
6292         return value;
6293     },
6294   */
6295     preFocus : function(){
6296         
6297         if(this.selectOnFocus){
6298             this.inputEl().dom.select();
6299         }
6300     },
6301     filterKeys : function(e){
6302         var k = e.getKey();
6303         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
6304             return;
6305         }
6306         var c = e.getCharCode(), cc = String.fromCharCode(c);
6307         if(Roo.isIE && (e.isSpecialKey() || !cc)){
6308             return;
6309         }
6310         if(!this.maskRe.test(cc)){
6311             e.stopEvent();
6312         }
6313     },
6314      /**
6315      * Clear any invalid styles/messages for this field
6316      */
6317     clearInvalid : function(){
6318         
6319         if(!this.el || this.preventMark){ // not rendered
6320             return;
6321         }
6322         this.el.removeClass(this.invalidClass);
6323         /*
6324         switch(this.msgTarget){
6325             case 'qtip':
6326                 this.el.dom.qtip = '';
6327                 break;
6328             case 'title':
6329                 this.el.dom.title = '';
6330                 break;
6331             case 'under':
6332                 if(this.errorEl){
6333                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
6334                 }
6335                 break;
6336             case 'side':
6337                 if(this.errorIcon){
6338                     this.errorIcon.dom.qtip = '';
6339                     this.errorIcon.hide();
6340                     this.un('resize', this.alignErrorIcon, this);
6341                 }
6342                 break;
6343             default:
6344                 var t = Roo.getDom(this.msgTarget);
6345                 t.innerHTML = '';
6346                 t.style.display = 'none';
6347                 break;
6348         }
6349         */
6350         this.fireEvent('valid', this);
6351     },
6352      /**
6353      * Mark this field as invalid
6354      * @param {String} msg The validation message
6355      */
6356     markInvalid : function(msg){
6357         if(!this.el  || this.preventMark){ // not rendered
6358             return;
6359         }
6360         this.el.addClass(this.invalidClass);
6361         /*
6362         msg = msg || this.invalidText;
6363         switch(this.msgTarget){
6364             case 'qtip':
6365                 this.el.dom.qtip = msg;
6366                 this.el.dom.qclass = 'x-form-invalid-tip';
6367                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
6368                     Roo.QuickTips.enable();
6369                 }
6370                 break;
6371             case 'title':
6372                 this.el.dom.title = msg;
6373                 break;
6374             case 'under':
6375                 if(!this.errorEl){
6376                     var elp = this.el.findParent('.x-form-element', 5, true);
6377                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
6378                     this.errorEl.setWidth(elp.getWidth(true)-20);
6379                 }
6380                 this.errorEl.update(msg);
6381                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
6382                 break;
6383             case 'side':
6384                 if(!this.errorIcon){
6385                     var elp = this.el.findParent('.x-form-element', 5, true);
6386                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
6387                 }
6388                 this.alignErrorIcon();
6389                 this.errorIcon.dom.qtip = msg;
6390                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
6391                 this.errorIcon.show();
6392                 this.on('resize', this.alignErrorIcon, this);
6393                 break;
6394             default:
6395                 var t = Roo.getDom(this.msgTarget);
6396                 t.innerHTML = msg;
6397                 t.style.display = this.msgDisplay;
6398                 break;
6399         }
6400         */
6401         this.fireEvent('invalid', this, msg);
6402     },
6403     // private
6404     SafariOnKeyDown : function(event)
6405     {
6406         // this is a workaround for a password hang bug on chrome/ webkit.
6407         
6408         var isSelectAll = false;
6409         
6410         if(this.inputEl().dom.selectionEnd > 0){
6411             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
6412         }
6413         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
6414             event.preventDefault();
6415             this.setValue('');
6416             return;
6417         }
6418         
6419         if(isSelectAll){ // backspace and delete key
6420             
6421             event.preventDefault();
6422             // this is very hacky as keydown always get's upper case.
6423             //
6424             var cc = String.fromCharCode(event.getCharCode());
6425             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
6426             
6427         }
6428     },
6429     adjustWidth : function(tag, w){
6430         tag = tag.toLowerCase();
6431         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
6432             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
6433                 if(tag == 'input'){
6434                     return w + 2;
6435                 }
6436                 if(tag == 'textarea'){
6437                     return w-2;
6438                 }
6439             }else if(Roo.isOpera){
6440                 if(tag == 'input'){
6441                     return w + 2;
6442                 }
6443                 if(tag == 'textarea'){
6444                     return w-2;
6445                 }
6446             }
6447         }
6448         return w;
6449     }
6450     
6451 });
6452
6453  
6454 /*
6455  * - LGPL
6456  *
6457  * Input
6458  * 
6459  */
6460
6461 /**
6462  * @class Roo.bootstrap.TextArea
6463  * @extends Roo.bootstrap.Input
6464  * Bootstrap TextArea class
6465  * @cfg {Number} cols Specifies the visible width of a text area
6466  * @cfg {Number} rows Specifies the visible number of lines in a text area
6467  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
6468  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
6469  * @cfg {string} html text
6470  * 
6471  * @constructor
6472  * Create a new TextArea
6473  * @param {Object} config The config object
6474  */
6475
6476 Roo.bootstrap.TextArea = function(config){
6477     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
6478    
6479 };
6480
6481 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
6482      
6483     cols : false,
6484     rows : 5,
6485     readOnly : false,
6486     warp : 'soft',
6487     resize : false,
6488     value: false,
6489     html: false,
6490     
6491     getAutoCreate : function(){
6492         
6493         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
6494         
6495         var id = Roo.id();
6496         
6497         var cfg = {};
6498         
6499         var input =  {
6500             tag: 'textarea',
6501             id : id,
6502             warp : this.warp,
6503             rows : this.rows,
6504             value : this.value || '',
6505             html: this.html || '',
6506             cls : 'form-control',
6507             placeholder : this.placeholder || '' 
6508             
6509         };
6510         
6511         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
6512             input.maxLength = this.maxLength;
6513         }
6514         
6515         if(this.resize){
6516             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
6517         }
6518         
6519         if(this.cols){
6520             input.cols = this.cols;
6521         }
6522         
6523         if (this.readOnly) {
6524             input.readonly = true;
6525         }
6526         
6527         if (this.name) {
6528             input.name = this.name;
6529         }
6530         
6531         if (this.size) {
6532             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
6533         }
6534         
6535         var settings=this;
6536         ['xs','sm','md','lg'].map(function(size){
6537             if (settings[size]) {
6538                 cfg.cls += ' col-' + size + '-' + settings[size];
6539             }
6540         });
6541         
6542         var inputblock = input;
6543         
6544         if (this.before || this.after) {
6545             
6546             inputblock = {
6547                 cls : 'input-group',
6548                 cn :  [] 
6549             };
6550             if (this.before) {
6551                 inputblock.cn.push({
6552                     tag :'span',
6553                     cls : 'input-group-addon',
6554                     html : this.before
6555                 });
6556             }
6557             inputblock.cn.push(input);
6558             if (this.after) {
6559                 inputblock.cn.push({
6560                     tag :'span',
6561                     cls : 'input-group-addon',
6562                     html : this.after
6563                 });
6564             }
6565             
6566         }
6567         
6568         if (align ==='left' && this.fieldLabel.length) {
6569                 Roo.log("left and has label");
6570                 cfg.cn = [
6571                     
6572                     {
6573                         tag: 'label',
6574                         'for' :  id,
6575                         cls : 'control-label col-sm-' + this.labelWidth,
6576                         html : this.fieldLabel
6577                         
6578                     },
6579                     {
6580                         cls : "col-sm-" + (12 - this.labelWidth), 
6581                         cn: [
6582                             inputblock
6583                         ]
6584                     }
6585                     
6586                 ];
6587         } else if ( this.fieldLabel.length) {
6588                 Roo.log(" label");
6589                  cfg.cn = [
6590                    
6591                     {
6592                         tag: 'label',
6593                         //cls : 'input-group-addon',
6594                         html : this.fieldLabel
6595                         
6596                     },
6597                     
6598                     inputblock
6599                     
6600                 ];
6601
6602         } else {
6603             
6604                    Roo.log(" no label && no align");
6605                 cfg.cn = [
6606                     
6607                         inputblock
6608                     
6609                 ];
6610                 
6611                 
6612         }
6613         
6614         if (this.disabled) {
6615             input.disabled=true;
6616         }
6617         
6618         return cfg;
6619         
6620     },
6621     /**
6622      * return the real textarea element.
6623      */
6624     inputEl: function ()
6625     {
6626         return this.el.select('textarea.form-control',true).first();
6627     }
6628 });
6629
6630  
6631 /*
6632  * - LGPL
6633  *
6634  * trigger field - base class for combo..
6635  * 
6636  */
6637  
6638 /**
6639  * @class Roo.bootstrap.TriggerField
6640  * @extends Roo.bootstrap.Input
6641  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
6642  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
6643  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
6644  * for which you can provide a custom implementation.  For example:
6645  * <pre><code>
6646 var trigger = new Roo.bootstrap.TriggerField();
6647 trigger.onTriggerClick = myTriggerFn;
6648 trigger.applyTo('my-field');
6649 </code></pre>
6650  *
6651  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
6652  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
6653  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
6654  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
6655  * @constructor
6656  * Create a new TriggerField.
6657  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
6658  * to the base TextField)
6659  */
6660 Roo.bootstrap.TriggerField = function(config){
6661     this.mimicing = false;
6662     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
6663 };
6664
6665 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
6666     /**
6667      * @cfg {String} triggerClass A CSS class to apply to the trigger
6668      */
6669      /**
6670      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
6671      */
6672     hideTrigger:false,
6673
6674     /** @cfg {Boolean} grow @hide */
6675     /** @cfg {Number} growMin @hide */
6676     /** @cfg {Number} growMax @hide */
6677
6678     /**
6679      * @hide 
6680      * @method
6681      */
6682     autoSize: Roo.emptyFn,
6683     // private
6684     monitorTab : true,
6685     // private
6686     deferHeight : true,
6687
6688     
6689     actionMode : 'wrap',
6690     
6691     
6692     
6693     getAutoCreate : function(){
6694        
6695         var parent = this.parent();
6696         
6697         var align = this.labelAlign || this.parentLabelAlign();
6698         
6699         var id = Roo.id();
6700         
6701         var cfg = {
6702             cls: 'form-group' //input-group
6703         };
6704         
6705         
6706         var input =  {
6707             tag: 'input',
6708             id : id,
6709             type : this.inputType,
6710             cls : 'form-control',
6711             autocomplete: 'off',
6712             placeholder : this.placeholder || '' 
6713             
6714         };
6715         if (this.name) {
6716             input.name = this.name;
6717         }
6718         if (this.size) {
6719             input.cls += ' input-' + this.size;
6720         }
6721         
6722         if (this.disabled) {
6723             input.disabled=true;
6724         }
6725         
6726         var inputblock = input;
6727         
6728         if (this.before || this.after) {
6729             
6730             inputblock = {
6731                 cls : 'input-group',
6732                 cn :  [] 
6733             };
6734             if (this.before) {
6735                 inputblock.cn.push({
6736                     tag :'span',
6737                     cls : 'input-group-addon',
6738                     html : this.before
6739                 });
6740             }
6741             inputblock.cn.push(input);
6742             if (this.after) {
6743                 inputblock.cn.push({
6744                     tag :'span',
6745                     cls : 'input-group-addon',
6746                     html : this.after
6747                 });
6748             }
6749             
6750         };
6751         
6752         var box = {
6753             tag: 'div',
6754             cn: [
6755                 {
6756                     tag: 'input',
6757                     type : 'hidden',
6758                     cls: 'form-hidden-field'
6759                 },
6760                 inputblock
6761             ]
6762             
6763         };
6764         
6765         if(this.multiple){
6766             Roo.log('multiple');
6767             
6768             box = {
6769                 tag: 'div',
6770                 cn: [
6771                     {
6772                         tag: 'input',
6773                         type : 'hidden',
6774                         cls: 'form-hidden-field'
6775                     },
6776                     {
6777                         tag: 'ul',
6778                         cls: 'select2-choices',
6779                         cn:[
6780                             {
6781                                 tag: 'li',
6782                                 cls: 'select2-search-field',
6783                                 cn: [
6784
6785                                     inputblock
6786                                 ]
6787                             }
6788                         ]
6789                     }
6790                 ]
6791             }
6792         };
6793         
6794         var combobox = {
6795             cls: 'select2-container input-group',
6796             cn: [
6797                 box,
6798                 {
6799                     tag: 'ul',
6800                     cls: 'typeahead typeahead-long dropdown-menu',
6801                     style: 'display:none'
6802                 }
6803             ]
6804         };
6805         
6806         if(!this.multiple){
6807             combobox.cn.push({
6808                 tag :'span',
6809                 cls : 'input-group-addon btn dropdown-toggle',
6810                 cn : [
6811                     {
6812                         tag: 'span',
6813                         cls: 'caret'
6814                     },
6815                     {
6816                         tag: 'span',
6817                         cls: 'combobox-clear',
6818                         cn  : [
6819                             {
6820                                 tag : 'i',
6821                                 cls: 'icon-remove'
6822                             }
6823                         ]
6824                     }
6825                 ]
6826
6827             })
6828         }
6829         
6830         if(this.multiple){
6831             combobox.cls += ' select2-container-multi';
6832         }
6833         
6834         if (align ==='left' && this.fieldLabel.length) {
6835             
6836                 Roo.log("left and has label");
6837                 cfg.cn = [
6838                     
6839                     {
6840                         tag: 'label',
6841                         'for' :  id,
6842                         cls : 'control-label col-sm-' + this.labelWidth,
6843                         html : this.fieldLabel
6844                         
6845                     },
6846                     {
6847                         cls : "col-sm-" + (12 - this.labelWidth), 
6848                         cn: [
6849                             combobox
6850                         ]
6851                     }
6852                     
6853                 ];
6854         } else if ( this.fieldLabel.length) {
6855                 Roo.log(" label");
6856                  cfg.cn = [
6857                    
6858                     {
6859                         tag: 'label',
6860                         //cls : 'input-group-addon',
6861                         html : this.fieldLabel
6862                         
6863                     },
6864                     
6865                     combobox
6866                     
6867                 ];
6868
6869         } else {
6870             
6871                 Roo.log(" no label && no align");
6872                 cfg = combobox
6873                      
6874                 
6875         }
6876          
6877         var settings=this;
6878         ['xs','sm','md','lg'].map(function(size){
6879             if (settings[size]) {
6880                 cfg.cls += ' col-' + size + '-' + settings[size];
6881             }
6882         });
6883         
6884         return cfg;
6885         
6886     },
6887     
6888     
6889     
6890     // private
6891     onResize : function(w, h){
6892 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
6893 //        if(typeof w == 'number'){
6894 //            var x = w - this.trigger.getWidth();
6895 //            this.inputEl().setWidth(this.adjustWidth('input', x));
6896 //            this.trigger.setStyle('left', x+'px');
6897 //        }
6898     },
6899
6900     // private
6901     adjustSize : Roo.BoxComponent.prototype.adjustSize,
6902
6903     // private
6904     getResizeEl : function(){
6905         return this.inputEl();
6906     },
6907
6908     // private
6909     getPositionEl : function(){
6910         return this.inputEl();
6911     },
6912
6913     // private
6914     alignErrorIcon : function(){
6915         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
6916     },
6917
6918     // private
6919     initEvents : function(){
6920         
6921         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
6922         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
6923         if(!this.multiple){
6924             this.trigger = this.el.select('span.dropdown-toggle',true).first();
6925             if(this.hideTrigger){
6926                 this.trigger.setDisplayed(false);
6927             }
6928             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
6929         }
6930         
6931         if(this.multiple){
6932             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
6933         }
6934         
6935         //this.trigger.addClassOnOver('x-form-trigger-over');
6936         //this.trigger.addClassOnClick('x-form-trigger-click');
6937         
6938         //if(!this.width){
6939         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
6940         //}
6941     },
6942
6943     // private
6944     initTrigger : function(){
6945        
6946     },
6947
6948     // private
6949     onDestroy : function(){
6950         if(this.trigger){
6951             this.trigger.removeAllListeners();
6952           //  this.trigger.remove();
6953         }
6954         //if(this.wrap){
6955         //    this.wrap.remove();
6956         //}
6957         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
6958     },
6959
6960     // private
6961     onFocus : function(){
6962         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
6963         /*
6964         if(!this.mimicing){
6965             this.wrap.addClass('x-trigger-wrap-focus');
6966             this.mimicing = true;
6967             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
6968             if(this.monitorTab){
6969                 this.el.on("keydown", this.checkTab, this);
6970             }
6971         }
6972         */
6973     },
6974
6975     // private
6976     checkTab : function(e){
6977         if(e.getKey() == e.TAB){
6978             this.triggerBlur();
6979         }
6980     },
6981
6982     // private
6983     onBlur : function(){
6984         // do nothing
6985     },
6986
6987     // private
6988     mimicBlur : function(e, t){
6989         /*
6990         if(!this.wrap.contains(t) && this.validateBlur()){
6991             this.triggerBlur();
6992         }
6993         */
6994     },
6995
6996     // private
6997     triggerBlur : function(){
6998         this.mimicing = false;
6999         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
7000         if(this.monitorTab){
7001             this.el.un("keydown", this.checkTab, this);
7002         }
7003         //this.wrap.removeClass('x-trigger-wrap-focus');
7004         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
7005     },
7006
7007     // private
7008     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
7009     validateBlur : function(e, t){
7010         return true;
7011     },
7012
7013     // private
7014     onDisable : function(){
7015         this.inputEl().dom.disabled = true;
7016         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
7017         //if(this.wrap){
7018         //    this.wrap.addClass('x-item-disabled');
7019         //}
7020     },
7021
7022     // private
7023     onEnable : function(){
7024         this.inputEl().dom.disabled = false;
7025         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
7026         //if(this.wrap){
7027         //    this.el.removeClass('x-item-disabled');
7028         //}
7029     },
7030
7031     // private
7032     onShow : function(){
7033         var ae = this.getActionEl();
7034         
7035         if(ae){
7036             ae.dom.style.display = '';
7037             ae.dom.style.visibility = 'visible';
7038         }
7039     },
7040
7041     // private
7042     
7043     onHide : function(){
7044         var ae = this.getActionEl();
7045         ae.dom.style.display = 'none';
7046     },
7047
7048     /**
7049      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
7050      * by an implementing function.
7051      * @method
7052      * @param {EventObject} e
7053      */
7054     onTriggerClick : Roo.emptyFn
7055 });
7056  /*
7057  * Based on:
7058  * Ext JS Library 1.1.1
7059  * Copyright(c) 2006-2007, Ext JS, LLC.
7060  *
7061  * Originally Released Under LGPL - original licence link has changed is not relivant.
7062  *
7063  * Fork - LGPL
7064  * <script type="text/javascript">
7065  */
7066
7067
7068 /**
7069  * @class Roo.data.SortTypes
7070  * @singleton
7071  * Defines the default sorting (casting?) comparison functions used when sorting data.
7072  */
7073 Roo.data.SortTypes = {
7074     /**
7075      * Default sort that does nothing
7076      * @param {Mixed} s The value being converted
7077      * @return {Mixed} The comparison value
7078      */
7079     none : function(s){
7080         return s;
7081     },
7082     
7083     /**
7084      * The regular expression used to strip tags
7085      * @type {RegExp}
7086      * @property
7087      */
7088     stripTagsRE : /<\/?[^>]+>/gi,
7089     
7090     /**
7091      * Strips all HTML tags to sort on text only
7092      * @param {Mixed} s The value being converted
7093      * @return {String} The comparison value
7094      */
7095     asText : function(s){
7096         return String(s).replace(this.stripTagsRE, "");
7097     },
7098     
7099     /**
7100      * Strips all HTML tags to sort on text only - Case insensitive
7101      * @param {Mixed} s The value being converted
7102      * @return {String} The comparison value
7103      */
7104     asUCText : function(s){
7105         return String(s).toUpperCase().replace(this.stripTagsRE, "");
7106     },
7107     
7108     /**
7109      * Case insensitive string
7110      * @param {Mixed} s The value being converted
7111      * @return {String} The comparison value
7112      */
7113     asUCString : function(s) {
7114         return String(s).toUpperCase();
7115     },
7116     
7117     /**
7118      * Date sorting
7119      * @param {Mixed} s The value being converted
7120      * @return {Number} The comparison value
7121      */
7122     asDate : function(s) {
7123         if(!s){
7124             return 0;
7125         }
7126         if(s instanceof Date){
7127             return s.getTime();
7128         }
7129         return Date.parse(String(s));
7130     },
7131     
7132     /**
7133      * Float sorting
7134      * @param {Mixed} s The value being converted
7135      * @return {Float} The comparison value
7136      */
7137     asFloat : function(s) {
7138         var val = parseFloat(String(s).replace(/,/g, ""));
7139         if(isNaN(val)) val = 0;
7140         return val;
7141     },
7142     
7143     /**
7144      * Integer sorting
7145      * @param {Mixed} s The value being converted
7146      * @return {Number} The comparison value
7147      */
7148     asInt : function(s) {
7149         var val = parseInt(String(s).replace(/,/g, ""));
7150         if(isNaN(val)) val = 0;
7151         return val;
7152     }
7153 };/*
7154  * Based on:
7155  * Ext JS Library 1.1.1
7156  * Copyright(c) 2006-2007, Ext JS, LLC.
7157  *
7158  * Originally Released Under LGPL - original licence link has changed is not relivant.
7159  *
7160  * Fork - LGPL
7161  * <script type="text/javascript">
7162  */
7163
7164 /**
7165 * @class Roo.data.Record
7166  * Instances of this class encapsulate both record <em>definition</em> information, and record
7167  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
7168  * to access Records cached in an {@link Roo.data.Store} object.<br>
7169  * <p>
7170  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
7171  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
7172  * objects.<br>
7173  * <p>
7174  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
7175  * @constructor
7176  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
7177  * {@link #create}. The parameters are the same.
7178  * @param {Array} data An associative Array of data values keyed by the field name.
7179  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
7180  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
7181  * not specified an integer id is generated.
7182  */
7183 Roo.data.Record = function(data, id){
7184     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
7185     this.data = data;
7186 };
7187
7188 /**
7189  * Generate a constructor for a specific record layout.
7190  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
7191  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
7192  * Each field definition object may contain the following properties: <ul>
7193  * <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,
7194  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
7195  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
7196  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
7197  * is being used, then this is a string containing the javascript expression to reference the data relative to 
7198  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
7199  * to the data item relative to the record element. If the mapping expression is the same as the field name,
7200  * this may be omitted.</p></li>
7201  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
7202  * <ul><li>auto (Default, implies no conversion)</li>
7203  * <li>string</li>
7204  * <li>int</li>
7205  * <li>float</li>
7206  * <li>boolean</li>
7207  * <li>date</li></ul></p></li>
7208  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
7209  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
7210  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
7211  * by the Reader into an object that will be stored in the Record. It is passed the
7212  * following parameters:<ul>
7213  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
7214  * </ul></p></li>
7215  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
7216  * </ul>
7217  * <br>usage:<br><pre><code>
7218 var TopicRecord = Roo.data.Record.create(
7219     {name: 'title', mapping: 'topic_title'},
7220     {name: 'author', mapping: 'username'},
7221     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
7222     {name: 'lastPost', mapping: 'post_time', type: 'date'},
7223     {name: 'lastPoster', mapping: 'user2'},
7224     {name: 'excerpt', mapping: 'post_text'}
7225 );
7226
7227 var myNewRecord = new TopicRecord({
7228     title: 'Do my job please',
7229     author: 'noobie',
7230     totalPosts: 1,
7231     lastPost: new Date(),
7232     lastPoster: 'Animal',
7233     excerpt: 'No way dude!'
7234 });
7235 myStore.add(myNewRecord);
7236 </code></pre>
7237  * @method create
7238  * @static
7239  */
7240 Roo.data.Record.create = function(o){
7241     var f = function(){
7242         f.superclass.constructor.apply(this, arguments);
7243     };
7244     Roo.extend(f, Roo.data.Record);
7245     var p = f.prototype;
7246     p.fields = new Roo.util.MixedCollection(false, function(field){
7247         return field.name;
7248     });
7249     for(var i = 0, len = o.length; i < len; i++){
7250         p.fields.add(new Roo.data.Field(o[i]));
7251     }
7252     f.getField = function(name){
7253         return p.fields.get(name);  
7254     };
7255     return f;
7256 };
7257
7258 Roo.data.Record.AUTO_ID = 1000;
7259 Roo.data.Record.EDIT = 'edit';
7260 Roo.data.Record.REJECT = 'reject';
7261 Roo.data.Record.COMMIT = 'commit';
7262
7263 Roo.data.Record.prototype = {
7264     /**
7265      * Readonly flag - true if this record has been modified.
7266      * @type Boolean
7267      */
7268     dirty : false,
7269     editing : false,
7270     error: null,
7271     modified: null,
7272
7273     // private
7274     join : function(store){
7275         this.store = store;
7276     },
7277
7278     /**
7279      * Set the named field to the specified value.
7280      * @param {String} name The name of the field to set.
7281      * @param {Object} value The value to set the field to.
7282      */
7283     set : function(name, value){
7284         if(this.data[name] == value){
7285             return;
7286         }
7287         this.dirty = true;
7288         if(!this.modified){
7289             this.modified = {};
7290         }
7291         if(typeof this.modified[name] == 'undefined'){
7292             this.modified[name] = this.data[name];
7293         }
7294         this.data[name] = value;
7295         if(!this.editing && this.store){
7296             this.store.afterEdit(this);
7297         }       
7298     },
7299
7300     /**
7301      * Get the value of the named field.
7302      * @param {String} name The name of the field to get the value of.
7303      * @return {Object} The value of the field.
7304      */
7305     get : function(name){
7306         return this.data[name]; 
7307     },
7308
7309     // private
7310     beginEdit : function(){
7311         this.editing = true;
7312         this.modified = {}; 
7313     },
7314
7315     // private
7316     cancelEdit : function(){
7317         this.editing = false;
7318         delete this.modified;
7319     },
7320
7321     // private
7322     endEdit : function(){
7323         this.editing = false;
7324         if(this.dirty && this.store){
7325             this.store.afterEdit(this);
7326         }
7327     },
7328
7329     /**
7330      * Usually called by the {@link Roo.data.Store} which owns the Record.
7331      * Rejects all changes made to the Record since either creation, or the last commit operation.
7332      * Modified fields are reverted to their original values.
7333      * <p>
7334      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
7335      * of reject operations.
7336      */
7337     reject : function(){
7338         var m = this.modified;
7339         for(var n in m){
7340             if(typeof m[n] != "function"){
7341                 this.data[n] = m[n];
7342             }
7343         }
7344         this.dirty = false;
7345         delete this.modified;
7346         this.editing = false;
7347         if(this.store){
7348             this.store.afterReject(this);
7349         }
7350     },
7351
7352     /**
7353      * Usually called by the {@link Roo.data.Store} which owns the Record.
7354      * Commits all changes made to the Record since either creation, or the last commit operation.
7355      * <p>
7356      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
7357      * of commit operations.
7358      */
7359     commit : function(){
7360         this.dirty = false;
7361         delete this.modified;
7362         this.editing = false;
7363         if(this.store){
7364             this.store.afterCommit(this);
7365         }
7366     },
7367
7368     // private
7369     hasError : function(){
7370         return this.error != null;
7371     },
7372
7373     // private
7374     clearError : function(){
7375         this.error = null;
7376     },
7377
7378     /**
7379      * Creates a copy of this record.
7380      * @param {String} id (optional) A new record id if you don't want to use this record's id
7381      * @return {Record}
7382      */
7383     copy : function(newId) {
7384         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
7385     }
7386 };/*
7387  * Based on:
7388  * Ext JS Library 1.1.1
7389  * Copyright(c) 2006-2007, Ext JS, LLC.
7390  *
7391  * Originally Released Under LGPL - original licence link has changed is not relivant.
7392  *
7393  * Fork - LGPL
7394  * <script type="text/javascript">
7395  */
7396
7397
7398
7399 /**
7400  * @class Roo.data.Store
7401  * @extends Roo.util.Observable
7402  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
7403  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
7404  * <p>
7405  * 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
7406  * has no knowledge of the format of the data returned by the Proxy.<br>
7407  * <p>
7408  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
7409  * instances from the data object. These records are cached and made available through accessor functions.
7410  * @constructor
7411  * Creates a new Store.
7412  * @param {Object} config A config object containing the objects needed for the Store to access data,
7413  * and read the data into Records.
7414  */
7415 Roo.data.Store = function(config){
7416     this.data = new Roo.util.MixedCollection(false);
7417     this.data.getKey = function(o){
7418         return o.id;
7419     };
7420     this.baseParams = {};
7421     // private
7422     this.paramNames = {
7423         "start" : "start",
7424         "limit" : "limit",
7425         "sort" : "sort",
7426         "dir" : "dir",
7427         "multisort" : "_multisort"
7428     };
7429
7430     if(config && config.data){
7431         this.inlineData = config.data;
7432         delete config.data;
7433     }
7434
7435     Roo.apply(this, config);
7436     
7437     if(this.reader){ // reader passed
7438         this.reader = Roo.factory(this.reader, Roo.data);
7439         this.reader.xmodule = this.xmodule || false;
7440         if(!this.recordType){
7441             this.recordType = this.reader.recordType;
7442         }
7443         if(this.reader.onMetaChange){
7444             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
7445         }
7446     }
7447
7448     if(this.recordType){
7449         this.fields = this.recordType.prototype.fields;
7450     }
7451     this.modified = [];
7452
7453     this.addEvents({
7454         /**
7455          * @event datachanged
7456          * Fires when the data cache has changed, and a widget which is using this Store
7457          * as a Record cache should refresh its view.
7458          * @param {Store} this
7459          */
7460         datachanged : true,
7461         /**
7462          * @event metachange
7463          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
7464          * @param {Store} this
7465          * @param {Object} meta The JSON metadata
7466          */
7467         metachange : true,
7468         /**
7469          * @event add
7470          * Fires when Records have been added to the Store
7471          * @param {Store} this
7472          * @param {Roo.data.Record[]} records The array of Records added
7473          * @param {Number} index The index at which the record(s) were added
7474          */
7475         add : true,
7476         /**
7477          * @event remove
7478          * Fires when a Record has been removed from the Store
7479          * @param {Store} this
7480          * @param {Roo.data.Record} record The Record that was removed
7481          * @param {Number} index The index at which the record was removed
7482          */
7483         remove : true,
7484         /**
7485          * @event update
7486          * Fires when a Record has been updated
7487          * @param {Store} this
7488          * @param {Roo.data.Record} record The Record that was updated
7489          * @param {String} operation The update operation being performed.  Value may be one of:
7490          * <pre><code>
7491  Roo.data.Record.EDIT
7492  Roo.data.Record.REJECT
7493  Roo.data.Record.COMMIT
7494          * </code></pre>
7495          */
7496         update : true,
7497         /**
7498          * @event clear
7499          * Fires when the data cache has been cleared.
7500          * @param {Store} this
7501          */
7502         clear : true,
7503         /**
7504          * @event beforeload
7505          * Fires before a request is made for a new data object.  If the beforeload handler returns false
7506          * the load action will be canceled.
7507          * @param {Store} this
7508          * @param {Object} options The loading options that were specified (see {@link #load} for details)
7509          */
7510         beforeload : true,
7511         /**
7512          * @event beforeloadadd
7513          * Fires after a new set of Records has been loaded.
7514          * @param {Store} this
7515          * @param {Roo.data.Record[]} records The Records that were loaded
7516          * @param {Object} options The loading options that were specified (see {@link #load} for details)
7517          */
7518         beforeloadadd : true,
7519         /**
7520          * @event load
7521          * Fires after a new set of Records has been loaded, before they are added to the store.
7522          * @param {Store} this
7523          * @param {Roo.data.Record[]} records The Records that were loaded
7524          * @param {Object} options The loading options that were specified (see {@link #load} for details)
7525          * @params {Object} return from reader
7526          */
7527         load : true,
7528         /**
7529          * @event loadexception
7530          * Fires if an exception occurs in the Proxy during loading.
7531          * Called with the signature of the Proxy's "loadexception" event.
7532          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
7533          * 
7534          * @param {Proxy} 
7535          * @param {Object} return from JsonData.reader() - success, totalRecords, records
7536          * @param {Object} load options 
7537          * @param {Object} jsonData from your request (normally this contains the Exception)
7538          */
7539         loadexception : true
7540     });
7541     
7542     if(this.proxy){
7543         this.proxy = Roo.factory(this.proxy, Roo.data);
7544         this.proxy.xmodule = this.xmodule || false;
7545         this.relayEvents(this.proxy,  ["loadexception"]);
7546     }
7547     this.sortToggle = {};
7548     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
7549
7550     Roo.data.Store.superclass.constructor.call(this);
7551
7552     if(this.inlineData){
7553         this.loadData(this.inlineData);
7554         delete this.inlineData;
7555     }
7556 };
7557
7558 Roo.extend(Roo.data.Store, Roo.util.Observable, {
7559      /**
7560     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
7561     * without a remote query - used by combo/forms at present.
7562     */
7563     
7564     /**
7565     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
7566     */
7567     /**
7568     * @cfg {Array} data Inline data to be loaded when the store is initialized.
7569     */
7570     /**
7571     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
7572     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
7573     */
7574     /**
7575     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
7576     * on any HTTP request
7577     */
7578     /**
7579     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
7580     */
7581     /**
7582     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
7583     */
7584     multiSort: false,
7585     /**
7586     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
7587     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
7588     */
7589     remoteSort : false,
7590
7591     /**
7592     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
7593      * loaded or when a record is removed. (defaults to false).
7594     */
7595     pruneModifiedRecords : false,
7596
7597     // private
7598     lastOptions : null,
7599
7600     /**
7601      * Add Records to the Store and fires the add event.
7602      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
7603      */
7604     add : function(records){
7605         records = [].concat(records);
7606         for(var i = 0, len = records.length; i < len; i++){
7607             records[i].join(this);
7608         }
7609         var index = this.data.length;
7610         this.data.addAll(records);
7611         this.fireEvent("add", this, records, index);
7612     },
7613
7614     /**
7615      * Remove a Record from the Store and fires the remove event.
7616      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
7617      */
7618     remove : function(record){
7619         var index = this.data.indexOf(record);
7620         this.data.removeAt(index);
7621         if(this.pruneModifiedRecords){
7622             this.modified.remove(record);
7623         }
7624         this.fireEvent("remove", this, record, index);
7625     },
7626
7627     /**
7628      * Remove all Records from the Store and fires the clear event.
7629      */
7630     removeAll : function(){
7631         this.data.clear();
7632         if(this.pruneModifiedRecords){
7633             this.modified = [];
7634         }
7635         this.fireEvent("clear", this);
7636     },
7637
7638     /**
7639      * Inserts Records to the Store at the given index and fires the add event.
7640      * @param {Number} index The start index at which to insert the passed Records.
7641      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
7642      */
7643     insert : function(index, records){
7644         records = [].concat(records);
7645         for(var i = 0, len = records.length; i < len; i++){
7646             this.data.insert(index, records[i]);
7647             records[i].join(this);
7648         }
7649         this.fireEvent("add", this, records, index);
7650     },
7651
7652     /**
7653      * Get the index within the cache of the passed Record.
7654      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
7655      * @return {Number} The index of the passed Record. Returns -1 if not found.
7656      */
7657     indexOf : function(record){
7658         return this.data.indexOf(record);
7659     },
7660
7661     /**
7662      * Get the index within the cache of the Record with the passed id.
7663      * @param {String} id The id of the Record to find.
7664      * @return {Number} The index of the Record. Returns -1 if not found.
7665      */
7666     indexOfId : function(id){
7667         return this.data.indexOfKey(id);
7668     },
7669
7670     /**
7671      * Get the Record with the specified id.
7672      * @param {String} id The id of the Record to find.
7673      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
7674      */
7675     getById : function(id){
7676         return this.data.key(id);
7677     },
7678
7679     /**
7680      * Get the Record at the specified index.
7681      * @param {Number} index The index of the Record to find.
7682      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
7683      */
7684     getAt : function(index){
7685         return this.data.itemAt(index);
7686     },
7687
7688     /**
7689      * Returns a range of Records between specified indices.
7690      * @param {Number} startIndex (optional) The starting index (defaults to 0)
7691      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
7692      * @return {Roo.data.Record[]} An array of Records
7693      */
7694     getRange : function(start, end){
7695         return this.data.getRange(start, end);
7696     },
7697
7698     // private
7699     storeOptions : function(o){
7700         o = Roo.apply({}, o);
7701         delete o.callback;
7702         delete o.scope;
7703         this.lastOptions = o;
7704     },
7705
7706     /**
7707      * Loads the Record cache from the configured Proxy using the configured Reader.
7708      * <p>
7709      * If using remote paging, then the first load call must specify the <em>start</em>
7710      * and <em>limit</em> properties in the options.params property to establish the initial
7711      * position within the dataset, and the number of Records to cache on each read from the Proxy.
7712      * <p>
7713      * <strong>It is important to note that for remote data sources, loading is asynchronous,
7714      * and this call will return before the new data has been loaded. Perform any post-processing
7715      * in a callback function, or in a "load" event handler.</strong>
7716      * <p>
7717      * @param {Object} options An object containing properties which control loading options:<ul>
7718      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
7719      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
7720      * passed the following arguments:<ul>
7721      * <li>r : Roo.data.Record[]</li>
7722      * <li>options: Options object from the load call</li>
7723      * <li>success: Boolean success indicator</li></ul></li>
7724      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
7725      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
7726      * </ul>
7727      */
7728     load : function(options){
7729         options = options || {};
7730         if(this.fireEvent("beforeload", this, options) !== false){
7731             this.storeOptions(options);
7732             var p = Roo.apply(options.params || {}, this.baseParams);
7733             // if meta was not loaded from remote source.. try requesting it.
7734             if (!this.reader.metaFromRemote) {
7735                 p._requestMeta = 1;
7736             }
7737             if(this.sortInfo && this.remoteSort){
7738                 var pn = this.paramNames;
7739                 p[pn["sort"]] = this.sortInfo.field;
7740                 p[pn["dir"]] = this.sortInfo.direction;
7741             }
7742             if (this.multiSort) {
7743                 var pn = this.paramNames;
7744                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
7745             }
7746             
7747             this.proxy.load(p, this.reader, this.loadRecords, this, options);
7748         }
7749     },
7750
7751     /**
7752      * Reloads the Record cache from the configured Proxy using the configured Reader and
7753      * the options from the last load operation performed.
7754      * @param {Object} options (optional) An object containing properties which may override the options
7755      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
7756      * the most recently used options are reused).
7757      */
7758     reload : function(options){
7759         this.load(Roo.applyIf(options||{}, this.lastOptions));
7760     },
7761
7762     // private
7763     // Called as a callback by the Reader during a load operation.
7764     loadRecords : function(o, options, success){
7765         if(!o || success === false){
7766             if(success !== false){
7767                 this.fireEvent("load", this, [], options, o);
7768             }
7769             if(options.callback){
7770                 options.callback.call(options.scope || this, [], options, false);
7771             }
7772             return;
7773         }
7774         // if data returned failure - throw an exception.
7775         if (o.success === false) {
7776             // show a message if no listener is registered.
7777             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
7778                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
7779             }
7780             // loadmask wil be hooked into this..
7781             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
7782             return;
7783         }
7784         var r = o.records, t = o.totalRecords || r.length;
7785         
7786         this.fireEvent("beforeloadadd", this, r, options, o);
7787         
7788         if(!options || options.add !== true){
7789             if(this.pruneModifiedRecords){
7790                 this.modified = [];
7791             }
7792             for(var i = 0, len = r.length; i < len; i++){
7793                 r[i].join(this);
7794             }
7795             if(this.snapshot){
7796                 this.data = this.snapshot;
7797                 delete this.snapshot;
7798             }
7799             this.data.clear();
7800             this.data.addAll(r);
7801             this.totalLength = t;
7802             this.applySort();
7803             this.fireEvent("datachanged", this);
7804         }else{
7805             this.totalLength = Math.max(t, this.data.length+r.length);
7806             this.add(r);
7807         }
7808         this.fireEvent("load", this, r, options, o);
7809         if(options.callback){
7810             options.callback.call(options.scope || this, r, options, true);
7811         }
7812     },
7813
7814
7815     /**
7816      * Loads data from a passed data block. A Reader which understands the format of the data
7817      * must have been configured in the constructor.
7818      * @param {Object} data The data block from which to read the Records.  The format of the data expected
7819      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
7820      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
7821      */
7822     loadData : function(o, append){
7823         var r = this.reader.readRecords(o);
7824         this.loadRecords(r, {add: append}, true);
7825     },
7826
7827     /**
7828      * Gets the number of cached records.
7829      * <p>
7830      * <em>If using paging, this may not be the total size of the dataset. If the data object
7831      * used by the Reader contains the dataset size, then the getTotalCount() function returns
7832      * the data set size</em>
7833      */
7834     getCount : function(){
7835         return this.data.length || 0;
7836     },
7837
7838     /**
7839      * Gets the total number of records in the dataset as returned by the server.
7840      * <p>
7841      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
7842      * the dataset size</em>
7843      */
7844     getTotalCount : function(){
7845         return this.totalLength || 0;
7846     },
7847
7848     /**
7849      * Returns the sort state of the Store as an object with two properties:
7850      * <pre><code>
7851  field {String} The name of the field by which the Records are sorted
7852  direction {String} The sort order, "ASC" or "DESC"
7853      * </code></pre>
7854      */
7855     getSortState : function(){
7856         return this.sortInfo;
7857     },
7858
7859     // private
7860     applySort : function(){
7861         if(this.sortInfo && !this.remoteSort){
7862             var s = this.sortInfo, f = s.field;
7863             var st = this.fields.get(f).sortType;
7864             var fn = function(r1, r2){
7865                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
7866                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
7867             };
7868             this.data.sort(s.direction, fn);
7869             if(this.snapshot && this.snapshot != this.data){
7870                 this.snapshot.sort(s.direction, fn);
7871             }
7872         }
7873     },
7874
7875     /**
7876      * Sets the default sort column and order to be used by the next load operation.
7877      * @param {String} fieldName The name of the field to sort by.
7878      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
7879      */
7880     setDefaultSort : function(field, dir){
7881         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
7882     },
7883
7884     /**
7885      * Sort the Records.
7886      * If remote sorting is used, the sort is performed on the server, and the cache is
7887      * reloaded. If local sorting is used, the cache is sorted internally.
7888      * @param {String} fieldName The name of the field to sort by.
7889      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
7890      */
7891     sort : function(fieldName, dir){
7892         var f = this.fields.get(fieldName);
7893         if(!dir){
7894             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
7895             
7896             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
7897                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
7898             }else{
7899                 dir = f.sortDir;
7900             }
7901         }
7902         this.sortToggle[f.name] = dir;
7903         this.sortInfo = {field: f.name, direction: dir};
7904         if(!this.remoteSort){
7905             this.applySort();
7906             this.fireEvent("datachanged", this);
7907         }else{
7908             this.load(this.lastOptions);
7909         }
7910     },
7911
7912     /**
7913      * Calls the specified function for each of the Records in the cache.
7914      * @param {Function} fn The function to call. The Record is passed as the first parameter.
7915      * Returning <em>false</em> aborts and exits the iteration.
7916      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
7917      */
7918     each : function(fn, scope){
7919         this.data.each(fn, scope);
7920     },
7921
7922     /**
7923      * Gets all records modified since the last commit.  Modified records are persisted across load operations
7924      * (e.g., during paging).
7925      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
7926      */
7927     getModifiedRecords : function(){
7928         return this.modified;
7929     },
7930
7931     // private
7932     createFilterFn : function(property, value, anyMatch){
7933         if(!value.exec){ // not a regex
7934             value = String(value);
7935             if(value.length == 0){
7936                 return false;
7937             }
7938             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
7939         }
7940         return function(r){
7941             return value.test(r.data[property]);
7942         };
7943     },
7944
7945     /**
7946      * Sums the value of <i>property</i> for each record between start and end and returns the result.
7947      * @param {String} property A field on your records
7948      * @param {Number} start The record index to start at (defaults to 0)
7949      * @param {Number} end The last record index to include (defaults to length - 1)
7950      * @return {Number} The sum
7951      */
7952     sum : function(property, start, end){
7953         var rs = this.data.items, v = 0;
7954         start = start || 0;
7955         end = (end || end === 0) ? end : rs.length-1;
7956
7957         for(var i = start; i <= end; i++){
7958             v += (rs[i].data[property] || 0);
7959         }
7960         return v;
7961     },
7962
7963     /**
7964      * Filter the records by a specified property.
7965      * @param {String} field A field on your records
7966      * @param {String/RegExp} value Either a string that the field
7967      * should start with or a RegExp to test against the field
7968      * @param {Boolean} anyMatch True to match any part not just the beginning
7969      */
7970     filter : function(property, value, anyMatch){
7971         var fn = this.createFilterFn(property, value, anyMatch);
7972         return fn ? this.filterBy(fn) : this.clearFilter();
7973     },
7974
7975     /**
7976      * Filter by a function. The specified function will be called with each
7977      * record in this data source. If the function returns true the record is included,
7978      * otherwise it is filtered.
7979      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
7980      * @param {Object} scope (optional) The scope of the function (defaults to this)
7981      */
7982     filterBy : function(fn, scope){
7983         this.snapshot = this.snapshot || this.data;
7984         this.data = this.queryBy(fn, scope||this);
7985         this.fireEvent("datachanged", this);
7986     },
7987
7988     /**
7989      * Query the records by a specified property.
7990      * @param {String} field A field on your records
7991      * @param {String/RegExp} value Either a string that the field
7992      * should start with or a RegExp to test against the field
7993      * @param {Boolean} anyMatch True to match any part not just the beginning
7994      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
7995      */
7996     query : function(property, value, anyMatch){
7997         var fn = this.createFilterFn(property, value, anyMatch);
7998         return fn ? this.queryBy(fn) : this.data.clone();
7999     },
8000
8001     /**
8002      * Query by a function. The specified function will be called with each
8003      * record in this data source. If the function returns true the record is included
8004      * in the results.
8005      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
8006      * @param {Object} scope (optional) The scope of the function (defaults to this)
8007       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
8008      **/
8009     queryBy : function(fn, scope){
8010         var data = this.snapshot || this.data;
8011         return data.filterBy(fn, scope||this);
8012     },
8013
8014     /**
8015      * Collects unique values for a particular dataIndex from this store.
8016      * @param {String} dataIndex The property to collect
8017      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
8018      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
8019      * @return {Array} An array of the unique values
8020      **/
8021     collect : function(dataIndex, allowNull, bypassFilter){
8022         var d = (bypassFilter === true && this.snapshot) ?
8023                 this.snapshot.items : this.data.items;
8024         var v, sv, r = [], l = {};
8025         for(var i = 0, len = d.length; i < len; i++){
8026             v = d[i].data[dataIndex];
8027             sv = String(v);
8028             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
8029                 l[sv] = true;
8030                 r[r.length] = v;
8031             }
8032         }
8033         return r;
8034     },
8035
8036     /**
8037      * Revert to a view of the Record cache with no filtering applied.
8038      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
8039      */
8040     clearFilter : function(suppressEvent){
8041         if(this.snapshot && this.snapshot != this.data){
8042             this.data = this.snapshot;
8043             delete this.snapshot;
8044             if(suppressEvent !== true){
8045                 this.fireEvent("datachanged", this);
8046             }
8047         }
8048     },
8049
8050     // private
8051     afterEdit : function(record){
8052         if(this.modified.indexOf(record) == -1){
8053             this.modified.push(record);
8054         }
8055         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
8056     },
8057     
8058     // private
8059     afterReject : function(record){
8060         this.modified.remove(record);
8061         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
8062     },
8063
8064     // private
8065     afterCommit : function(record){
8066         this.modified.remove(record);
8067         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
8068     },
8069
8070     /**
8071      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
8072      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
8073      */
8074     commitChanges : function(){
8075         var m = this.modified.slice(0);
8076         this.modified = [];
8077         for(var i = 0, len = m.length; i < len; i++){
8078             m[i].commit();
8079         }
8080     },
8081
8082     /**
8083      * Cancel outstanding changes on all changed records.
8084      */
8085     rejectChanges : function(){
8086         var m = this.modified.slice(0);
8087         this.modified = [];
8088         for(var i = 0, len = m.length; i < len; i++){
8089             m[i].reject();
8090         }
8091     },
8092
8093     onMetaChange : function(meta, rtype, o){
8094         this.recordType = rtype;
8095         this.fields = rtype.prototype.fields;
8096         delete this.snapshot;
8097         this.sortInfo = meta.sortInfo || this.sortInfo;
8098         this.modified = [];
8099         this.fireEvent('metachange', this, this.reader.meta);
8100     },
8101     
8102     moveIndex : function(data, type)
8103     {
8104         var index = this.indexOf(data);
8105         
8106         var newIndex = index + type;
8107         
8108         this.remove(data);
8109         
8110         this.insert(newIndex, data);
8111         
8112     }
8113 });/*
8114  * Based on:
8115  * Ext JS Library 1.1.1
8116  * Copyright(c) 2006-2007, Ext JS, LLC.
8117  *
8118  * Originally Released Under LGPL - original licence link has changed is not relivant.
8119  *
8120  * Fork - LGPL
8121  * <script type="text/javascript">
8122  */
8123
8124 /**
8125  * @class Roo.data.SimpleStore
8126  * @extends Roo.data.Store
8127  * Small helper class to make creating Stores from Array data easier.
8128  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
8129  * @cfg {Array} fields An array of field definition objects, or field name strings.
8130  * @cfg {Array} data The multi-dimensional array of data
8131  * @constructor
8132  * @param {Object} config
8133  */
8134 Roo.data.SimpleStore = function(config){
8135     Roo.data.SimpleStore.superclass.constructor.call(this, {
8136         isLocal : true,
8137         reader: new Roo.data.ArrayReader({
8138                 id: config.id
8139             },
8140             Roo.data.Record.create(config.fields)
8141         ),
8142         proxy : new Roo.data.MemoryProxy(config.data)
8143     });
8144     this.load();
8145 };
8146 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
8147  * Based on:
8148  * Ext JS Library 1.1.1
8149  * Copyright(c) 2006-2007, Ext JS, LLC.
8150  *
8151  * Originally Released Under LGPL - original licence link has changed is not relivant.
8152  *
8153  * Fork - LGPL
8154  * <script type="text/javascript">
8155  */
8156
8157 /**
8158 /**
8159  * @extends Roo.data.Store
8160  * @class Roo.data.JsonStore
8161  * Small helper class to make creating Stores for JSON data easier. <br/>
8162 <pre><code>
8163 var store = new Roo.data.JsonStore({
8164     url: 'get-images.php',
8165     root: 'images',
8166     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
8167 });
8168 </code></pre>
8169  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
8170  * JsonReader and HttpProxy (unless inline data is provided).</b>
8171  * @cfg {Array} fields An array of field definition objects, or field name strings.
8172  * @constructor
8173  * @param {Object} config
8174  */
8175 Roo.data.JsonStore = function(c){
8176     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
8177         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
8178         reader: new Roo.data.JsonReader(c, c.fields)
8179     }));
8180 };
8181 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
8182  * Based on:
8183  * Ext JS Library 1.1.1
8184  * Copyright(c) 2006-2007, Ext JS, LLC.
8185  *
8186  * Originally Released Under LGPL - original licence link has changed is not relivant.
8187  *
8188  * Fork - LGPL
8189  * <script type="text/javascript">
8190  */
8191
8192  
8193 Roo.data.Field = function(config){
8194     if(typeof config == "string"){
8195         config = {name: config};
8196     }
8197     Roo.apply(this, config);
8198     
8199     if(!this.type){
8200         this.type = "auto";
8201     }
8202     
8203     var st = Roo.data.SortTypes;
8204     // named sortTypes are supported, here we look them up
8205     if(typeof this.sortType == "string"){
8206         this.sortType = st[this.sortType];
8207     }
8208     
8209     // set default sortType for strings and dates
8210     if(!this.sortType){
8211         switch(this.type){
8212             case "string":
8213                 this.sortType = st.asUCString;
8214                 break;
8215             case "date":
8216                 this.sortType = st.asDate;
8217                 break;
8218             default:
8219                 this.sortType = st.none;
8220         }
8221     }
8222
8223     // define once
8224     var stripRe = /[\$,%]/g;
8225
8226     // prebuilt conversion function for this field, instead of
8227     // switching every time we're reading a value
8228     if(!this.convert){
8229         var cv, dateFormat = this.dateFormat;
8230         switch(this.type){
8231             case "":
8232             case "auto":
8233             case undefined:
8234                 cv = function(v){ return v; };
8235                 break;
8236             case "string":
8237                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
8238                 break;
8239             case "int":
8240                 cv = function(v){
8241                     return v !== undefined && v !== null && v !== '' ?
8242                            parseInt(String(v).replace(stripRe, ""), 10) : '';
8243                     };
8244                 break;
8245             case "float":
8246                 cv = function(v){
8247                     return v !== undefined && v !== null && v !== '' ?
8248                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
8249                     };
8250                 break;
8251             case "bool":
8252             case "boolean":
8253                 cv = function(v){ return v === true || v === "true" || v == 1; };
8254                 break;
8255             case "date":
8256                 cv = function(v){
8257                     if(!v){
8258                         return '';
8259                     }
8260                     if(v instanceof Date){
8261                         return v;
8262                     }
8263                     if(dateFormat){
8264                         if(dateFormat == "timestamp"){
8265                             return new Date(v*1000);
8266                         }
8267                         return Date.parseDate(v, dateFormat);
8268                     }
8269                     var parsed = Date.parse(v);
8270                     return parsed ? new Date(parsed) : null;
8271                 };
8272              break;
8273             
8274         }
8275         this.convert = cv;
8276     }
8277 };
8278
8279 Roo.data.Field.prototype = {
8280     dateFormat: null,
8281     defaultValue: "",
8282     mapping: null,
8283     sortType : null,
8284     sortDir : "ASC"
8285 };/*
8286  * Based on:
8287  * Ext JS Library 1.1.1
8288  * Copyright(c) 2006-2007, Ext JS, LLC.
8289  *
8290  * Originally Released Under LGPL - original licence link has changed is not relivant.
8291  *
8292  * Fork - LGPL
8293  * <script type="text/javascript">
8294  */
8295  
8296 // Base class for reading structured data from a data source.  This class is intended to be
8297 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
8298
8299 /**
8300  * @class Roo.data.DataReader
8301  * Base class for reading structured data from a data source.  This class is intended to be
8302  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
8303  */
8304
8305 Roo.data.DataReader = function(meta, recordType){
8306     
8307     this.meta = meta;
8308     
8309     this.recordType = recordType instanceof Array ? 
8310         Roo.data.Record.create(recordType) : recordType;
8311 };
8312
8313 Roo.data.DataReader.prototype = {
8314      /**
8315      * Create an empty record
8316      * @param {Object} data (optional) - overlay some values
8317      * @return {Roo.data.Record} record created.
8318      */
8319     newRow :  function(d) {
8320         var da =  {};
8321         this.recordType.prototype.fields.each(function(c) {
8322             switch( c.type) {
8323                 case 'int' : da[c.name] = 0; break;
8324                 case 'date' : da[c.name] = new Date(); break;
8325                 case 'float' : da[c.name] = 0.0; break;
8326                 case 'boolean' : da[c.name] = false; break;
8327                 default : da[c.name] = ""; break;
8328             }
8329             
8330         });
8331         return new this.recordType(Roo.apply(da, d));
8332     }
8333     
8334 };/*
8335  * Based on:
8336  * Ext JS Library 1.1.1
8337  * Copyright(c) 2006-2007, Ext JS, LLC.
8338  *
8339  * Originally Released Under LGPL - original licence link has changed is not relivant.
8340  *
8341  * Fork - LGPL
8342  * <script type="text/javascript">
8343  */
8344
8345 /**
8346  * @class Roo.data.DataProxy
8347  * @extends Roo.data.Observable
8348  * This class is an abstract base class for implementations which provide retrieval of
8349  * unformatted data objects.<br>
8350  * <p>
8351  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
8352  * (of the appropriate type which knows how to parse the data object) to provide a block of
8353  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
8354  * <p>
8355  * Custom implementations must implement the load method as described in
8356  * {@link Roo.data.HttpProxy#load}.
8357  */
8358 Roo.data.DataProxy = function(){
8359     this.addEvents({
8360         /**
8361          * @event beforeload
8362          * Fires before a network request is made to retrieve a data object.
8363          * @param {Object} This DataProxy object.
8364          * @param {Object} params The params parameter to the load function.
8365          */
8366         beforeload : true,
8367         /**
8368          * @event load
8369          * Fires before the load method's callback is called.
8370          * @param {Object} This DataProxy object.
8371          * @param {Object} o The data object.
8372          * @param {Object} arg The callback argument object passed to the load function.
8373          */
8374         load : true,
8375         /**
8376          * @event loadexception
8377          * Fires if an Exception occurs during data retrieval.
8378          * @param {Object} This DataProxy object.
8379          * @param {Object} o The data object.
8380          * @param {Object} arg The callback argument object passed to the load function.
8381          * @param {Object} e The Exception.
8382          */
8383         loadexception : true
8384     });
8385     Roo.data.DataProxy.superclass.constructor.call(this);
8386 };
8387
8388 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
8389
8390     /**
8391      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
8392      */
8393 /*
8394  * Based on:
8395  * Ext JS Library 1.1.1
8396  * Copyright(c) 2006-2007, Ext JS, LLC.
8397  *
8398  * Originally Released Under LGPL - original licence link has changed is not relivant.
8399  *
8400  * Fork - LGPL
8401  * <script type="text/javascript">
8402  */
8403 /**
8404  * @class Roo.data.MemoryProxy
8405  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
8406  * to the Reader when its load method is called.
8407  * @constructor
8408  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
8409  */
8410 Roo.data.MemoryProxy = function(data){
8411     if (data.data) {
8412         data = data.data;
8413     }
8414     Roo.data.MemoryProxy.superclass.constructor.call(this);
8415     this.data = data;
8416 };
8417
8418 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
8419     /**
8420      * Load data from the requested source (in this case an in-memory
8421      * data object passed to the constructor), read the data object into
8422      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
8423      * process that block using the passed callback.
8424      * @param {Object} params This parameter is not used by the MemoryProxy class.
8425      * @param {Roo.data.DataReader} reader The Reader object which converts the data
8426      * object into a block of Roo.data.Records.
8427      * @param {Function} callback The function into which to pass the block of Roo.data.records.
8428      * The function must be passed <ul>
8429      * <li>The Record block object</li>
8430      * <li>The "arg" argument from the load function</li>
8431      * <li>A boolean success indicator</li>
8432      * </ul>
8433      * @param {Object} scope The scope in which to call the callback
8434      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
8435      */
8436     load : function(params, reader, callback, scope, arg){
8437         params = params || {};
8438         var result;
8439         try {
8440             result = reader.readRecords(this.data);
8441         }catch(e){
8442             this.fireEvent("loadexception", this, arg, null, e);
8443             callback.call(scope, null, arg, false);
8444             return;
8445         }
8446         callback.call(scope, result, arg, true);
8447     },
8448     
8449     // private
8450     update : function(params, records){
8451         
8452     }
8453 });/*
8454  * Based on:
8455  * Ext JS Library 1.1.1
8456  * Copyright(c) 2006-2007, Ext JS, LLC.
8457  *
8458  * Originally Released Under LGPL - original licence link has changed is not relivant.
8459  *
8460  * Fork - LGPL
8461  * <script type="text/javascript">
8462  */
8463 /**
8464  * @class Roo.data.HttpProxy
8465  * @extends Roo.data.DataProxy
8466  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
8467  * configured to reference a certain URL.<br><br>
8468  * <p>
8469  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
8470  * from which the running page was served.<br><br>
8471  * <p>
8472  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
8473  * <p>
8474  * Be aware that to enable the browser to parse an XML document, the server must set
8475  * the Content-Type header in the HTTP response to "text/xml".
8476  * @constructor
8477  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
8478  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
8479  * will be used to make the request.
8480  */
8481 Roo.data.HttpProxy = function(conn){
8482     Roo.data.HttpProxy.superclass.constructor.call(this);
8483     // is conn a conn config or a real conn?
8484     this.conn = conn;
8485     this.useAjax = !conn || !conn.events;
8486   
8487 };
8488
8489 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
8490     // thse are take from connection...
8491     
8492     /**
8493      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
8494      */
8495     /**
8496      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
8497      * extra parameters to each request made by this object. (defaults to undefined)
8498      */
8499     /**
8500      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
8501      *  to each request made by this object. (defaults to undefined)
8502      */
8503     /**
8504      * @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)
8505      */
8506     /**
8507      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
8508      */
8509      /**
8510      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
8511      * @type Boolean
8512      */
8513   
8514
8515     /**
8516      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
8517      * @type Boolean
8518      */
8519     /**
8520      * Return the {@link Roo.data.Connection} object being used by this Proxy.
8521      * @return {Connection} The Connection object. This object may be used to subscribe to events on
8522      * a finer-grained basis than the DataProxy events.
8523      */
8524     getConnection : function(){
8525         return this.useAjax ? Roo.Ajax : this.conn;
8526     },
8527
8528     /**
8529      * Load data from the configured {@link Roo.data.Connection}, read the data object into
8530      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
8531      * process that block using the passed callback.
8532      * @param {Object} params An object containing properties which are to be used as HTTP parameters
8533      * for the request to the remote server.
8534      * @param {Roo.data.DataReader} reader The Reader object which converts the data
8535      * object into a block of Roo.data.Records.
8536      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
8537      * The function must be passed <ul>
8538      * <li>The Record block object</li>
8539      * <li>The "arg" argument from the load function</li>
8540      * <li>A boolean success indicator</li>
8541      * </ul>
8542      * @param {Object} scope The scope in which to call the callback
8543      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
8544      */
8545     load : function(params, reader, callback, scope, arg){
8546         if(this.fireEvent("beforeload", this, params) !== false){
8547             var  o = {
8548                 params : params || {},
8549                 request: {
8550                     callback : callback,
8551                     scope : scope,
8552                     arg : arg
8553                 },
8554                 reader: reader,
8555                 callback : this.loadResponse,
8556                 scope: this
8557             };
8558             if(this.useAjax){
8559                 Roo.applyIf(o, this.conn);
8560                 if(this.activeRequest){
8561                     Roo.Ajax.abort(this.activeRequest);
8562                 }
8563                 this.activeRequest = Roo.Ajax.request(o);
8564             }else{
8565                 this.conn.request(o);
8566             }
8567         }else{
8568             callback.call(scope||this, null, arg, false);
8569         }
8570     },
8571
8572     // private
8573     loadResponse : function(o, success, response){
8574         delete this.activeRequest;
8575         if(!success){
8576             this.fireEvent("loadexception", this, o, response);
8577             o.request.callback.call(o.request.scope, null, o.request.arg, false);
8578             return;
8579         }
8580         var result;
8581         try {
8582             result = o.reader.read(response);
8583         }catch(e){
8584             this.fireEvent("loadexception", this, o, response, e);
8585             o.request.callback.call(o.request.scope, null, o.request.arg, false);
8586             return;
8587         }
8588         
8589         this.fireEvent("load", this, o, o.request.arg);
8590         o.request.callback.call(o.request.scope, result, o.request.arg, true);
8591     },
8592
8593     // private
8594     update : function(dataSet){
8595
8596     },
8597
8598     // private
8599     updateResponse : function(dataSet){
8600
8601     }
8602 });/*
8603  * Based on:
8604  * Ext JS Library 1.1.1
8605  * Copyright(c) 2006-2007, Ext JS, LLC.
8606  *
8607  * Originally Released Under LGPL - original licence link has changed is not relivant.
8608  *
8609  * Fork - LGPL
8610  * <script type="text/javascript">
8611  */
8612
8613 /**
8614  * @class Roo.data.ScriptTagProxy
8615  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
8616  * other than the originating domain of the running page.<br><br>
8617  * <p>
8618  * <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
8619  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
8620  * <p>
8621  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
8622  * source code that is used as the source inside a &lt;script> tag.<br><br>
8623  * <p>
8624  * In order for the browser to process the returned data, the server must wrap the data object
8625  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
8626  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
8627  * depending on whether the callback name was passed:
8628  * <p>
8629  * <pre><code>
8630 boolean scriptTag = false;
8631 String cb = request.getParameter("callback");
8632 if (cb != null) {
8633     scriptTag = true;
8634     response.setContentType("text/javascript");
8635 } else {
8636     response.setContentType("application/x-json");
8637 }
8638 Writer out = response.getWriter();
8639 if (scriptTag) {
8640     out.write(cb + "(");
8641 }
8642 out.print(dataBlock.toJsonString());
8643 if (scriptTag) {
8644     out.write(");");
8645 }
8646 </pre></code>
8647  *
8648  * @constructor
8649  * @param {Object} config A configuration object.
8650  */
8651 Roo.data.ScriptTagProxy = function(config){
8652     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
8653     Roo.apply(this, config);
8654     this.head = document.getElementsByTagName("head")[0];
8655 };
8656
8657 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
8658
8659 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
8660     /**
8661      * @cfg {String} url The URL from which to request the data object.
8662      */
8663     /**
8664      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
8665      */
8666     timeout : 30000,
8667     /**
8668      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
8669      * the server the name of the callback function set up by the load call to process the returned data object.
8670      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
8671      * javascript output which calls this named function passing the data object as its only parameter.
8672      */
8673     callbackParam : "callback",
8674     /**
8675      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
8676      * name to the request.
8677      */
8678     nocache : true,
8679
8680     /**
8681      * Load data from the configured URL, read the data object into
8682      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
8683      * process that block using the passed callback.
8684      * @param {Object} params An object containing properties which are to be used as HTTP parameters
8685      * for the request to the remote server.
8686      * @param {Roo.data.DataReader} reader The Reader object which converts the data
8687      * object into a block of Roo.data.Records.
8688      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
8689      * The function must be passed <ul>
8690      * <li>The Record block object</li>
8691      * <li>The "arg" argument from the load function</li>
8692      * <li>A boolean success indicator</li>
8693      * </ul>
8694      * @param {Object} scope The scope in which to call the callback
8695      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
8696      */
8697     load : function(params, reader, callback, scope, arg){
8698         if(this.fireEvent("beforeload", this, params) !== false){
8699
8700             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
8701
8702             var url = this.url;
8703             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
8704             if(this.nocache){
8705                 url += "&_dc=" + (new Date().getTime());
8706             }
8707             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
8708             var trans = {
8709                 id : transId,
8710                 cb : "stcCallback"+transId,
8711                 scriptId : "stcScript"+transId,
8712                 params : params,
8713                 arg : arg,
8714                 url : url,
8715                 callback : callback,
8716                 scope : scope,
8717                 reader : reader
8718             };
8719             var conn = this;
8720
8721             window[trans.cb] = function(o){
8722                 conn.handleResponse(o, trans);
8723             };
8724
8725             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
8726
8727             if(this.autoAbort !== false){
8728                 this.abort();
8729             }
8730
8731             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
8732
8733             var script = document.createElement("script");
8734             script.setAttribute("src", url);
8735             script.setAttribute("type", "text/javascript");
8736             script.setAttribute("id", trans.scriptId);
8737             this.head.appendChild(script);
8738
8739             this.trans = trans;
8740         }else{
8741             callback.call(scope||this, null, arg, false);
8742         }
8743     },
8744
8745     // private
8746     isLoading : function(){
8747         return this.trans ? true : false;
8748     },
8749
8750     /**
8751      * Abort the current server request.
8752      */
8753     abort : function(){
8754         if(this.isLoading()){
8755             this.destroyTrans(this.trans);
8756         }
8757     },
8758
8759     // private
8760     destroyTrans : function(trans, isLoaded){
8761         this.head.removeChild(document.getElementById(trans.scriptId));
8762         clearTimeout(trans.timeoutId);
8763         if(isLoaded){
8764             window[trans.cb] = undefined;
8765             try{
8766                 delete window[trans.cb];
8767             }catch(e){}
8768         }else{
8769             // if hasn't been loaded, wait for load to remove it to prevent script error
8770             window[trans.cb] = function(){
8771                 window[trans.cb] = undefined;
8772                 try{
8773                     delete window[trans.cb];
8774                 }catch(e){}
8775             };
8776         }
8777     },
8778
8779     // private
8780     handleResponse : function(o, trans){
8781         this.trans = false;
8782         this.destroyTrans(trans, true);
8783         var result;
8784         try {
8785             result = trans.reader.readRecords(o);
8786         }catch(e){
8787             this.fireEvent("loadexception", this, o, trans.arg, e);
8788             trans.callback.call(trans.scope||window, null, trans.arg, false);
8789             return;
8790         }
8791         this.fireEvent("load", this, o, trans.arg);
8792         trans.callback.call(trans.scope||window, result, trans.arg, true);
8793     },
8794
8795     // private
8796     handleFailure : function(trans){
8797         this.trans = false;
8798         this.destroyTrans(trans, false);
8799         this.fireEvent("loadexception", this, null, trans.arg);
8800         trans.callback.call(trans.scope||window, null, trans.arg, false);
8801     }
8802 });/*
8803  * Based on:
8804  * Ext JS Library 1.1.1
8805  * Copyright(c) 2006-2007, Ext JS, LLC.
8806  *
8807  * Originally Released Under LGPL - original licence link has changed is not relivant.
8808  *
8809  * Fork - LGPL
8810  * <script type="text/javascript">
8811  */
8812
8813 /**
8814  * @class Roo.data.JsonReader
8815  * @extends Roo.data.DataReader
8816  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
8817  * based on mappings in a provided Roo.data.Record constructor.
8818  * 
8819  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
8820  * in the reply previously. 
8821  * 
8822  * <p>
8823  * Example code:
8824  * <pre><code>
8825 var RecordDef = Roo.data.Record.create([
8826     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
8827     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
8828 ]);
8829 var myReader = new Roo.data.JsonReader({
8830     totalProperty: "results",    // The property which contains the total dataset size (optional)
8831     root: "rows",                // The property which contains an Array of row objects
8832     id: "id"                     // The property within each row object that provides an ID for the record (optional)
8833 }, RecordDef);
8834 </code></pre>
8835  * <p>
8836  * This would consume a JSON file like this:
8837  * <pre><code>
8838 { 'results': 2, 'rows': [
8839     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
8840     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
8841 }
8842 </code></pre>
8843  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
8844  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
8845  * paged from the remote server.
8846  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
8847  * @cfg {String} root name of the property which contains the Array of row objects.
8848  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
8849  * @constructor
8850  * Create a new JsonReader
8851  * @param {Object} meta Metadata configuration options
8852  * @param {Object} recordType Either an Array of field definition objects,
8853  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
8854  */
8855 Roo.data.JsonReader = function(meta, recordType){
8856     
8857     meta = meta || {};
8858     // set some defaults:
8859     Roo.applyIf(meta, {
8860         totalProperty: 'total',
8861         successProperty : 'success',
8862         root : 'data',
8863         id : 'id'
8864     });
8865     
8866     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
8867 };
8868 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
8869     
8870     /**
8871      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
8872      * Used by Store query builder to append _requestMeta to params.
8873      * 
8874      */
8875     metaFromRemote : false,
8876     /**
8877      * This method is only used by a DataProxy which has retrieved data from a remote server.
8878      * @param {Object} response The XHR object which contains the JSON data in its responseText.
8879      * @return {Object} data A data block which is used by an Roo.data.Store object as
8880      * a cache of Roo.data.Records.
8881      */
8882     read : function(response){
8883         var json = response.responseText;
8884        
8885         var o = /* eval:var:o */ eval("("+json+")");
8886         if(!o) {
8887             throw {message: "JsonReader.read: Json object not found"};
8888         }
8889         
8890         if(o.metaData){
8891             
8892             delete this.ef;
8893             this.metaFromRemote = true;
8894             this.meta = o.metaData;
8895             this.recordType = Roo.data.Record.create(o.metaData.fields);
8896             this.onMetaChange(this.meta, this.recordType, o);
8897         }
8898         return this.readRecords(o);
8899     },
8900
8901     // private function a store will implement
8902     onMetaChange : function(meta, recordType, o){
8903
8904     },
8905
8906     /**
8907          * @ignore
8908          */
8909     simpleAccess: function(obj, subsc) {
8910         return obj[subsc];
8911     },
8912
8913         /**
8914          * @ignore
8915          */
8916     getJsonAccessor: function(){
8917         var re = /[\[\.]/;
8918         return function(expr) {
8919             try {
8920                 return(re.test(expr))
8921                     ? new Function("obj", "return obj." + expr)
8922                     : function(obj){
8923                         return obj[expr];
8924                     };
8925             } catch(e){}
8926             return Roo.emptyFn;
8927         };
8928     }(),
8929
8930     /**
8931      * Create a data block containing Roo.data.Records from an XML document.
8932      * @param {Object} o An object which contains an Array of row objects in the property specified
8933      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
8934      * which contains the total size of the dataset.
8935      * @return {Object} data A data block which is used by an Roo.data.Store object as
8936      * a cache of Roo.data.Records.
8937      */
8938     readRecords : function(o){
8939         /**
8940          * After any data loads, the raw JSON data is available for further custom processing.
8941          * @type Object
8942          */
8943         this.o = o;
8944         var s = this.meta, Record = this.recordType,
8945             f = Record.prototype.fields, fi = f.items, fl = f.length;
8946
8947 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
8948         if (!this.ef) {
8949             if(s.totalProperty) {
8950                     this.getTotal = this.getJsonAccessor(s.totalProperty);
8951                 }
8952                 if(s.successProperty) {
8953                     this.getSuccess = this.getJsonAccessor(s.successProperty);
8954                 }
8955                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
8956                 if (s.id) {
8957                         var g = this.getJsonAccessor(s.id);
8958                         this.getId = function(rec) {
8959                                 var r = g(rec);
8960                                 return (r === undefined || r === "") ? null : r;
8961                         };
8962                 } else {
8963                         this.getId = function(){return null;};
8964                 }
8965             this.ef = [];
8966             for(var jj = 0; jj < fl; jj++){
8967                 f = fi[jj];
8968                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
8969                 this.ef[jj] = this.getJsonAccessor(map);
8970             }
8971         }
8972
8973         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
8974         if(s.totalProperty){
8975             var vt = parseInt(this.getTotal(o), 10);
8976             if(!isNaN(vt)){
8977                 totalRecords = vt;
8978             }
8979         }
8980         if(s.successProperty){
8981             var vs = this.getSuccess(o);
8982             if(vs === false || vs === 'false'){
8983                 success = false;
8984             }
8985         }
8986         var records = [];
8987             for(var i = 0; i < c; i++){
8988                     var n = root[i];
8989                 var values = {};
8990                 var id = this.getId(n);
8991                 for(var j = 0; j < fl; j++){
8992                     f = fi[j];
8993                 var v = this.ef[j](n);
8994                 if (!f.convert) {
8995                     Roo.log('missing convert for ' + f.name);
8996                     Roo.log(f);
8997                     continue;
8998                 }
8999                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
9000                 }
9001                 var record = new Record(values, id);
9002                 record.json = n;
9003                 records[i] = record;
9004             }
9005             return {
9006             raw : o,
9007                 success : success,
9008                 records : records,
9009                 totalRecords : totalRecords
9010             };
9011     }
9012 });/*
9013  * Based on:
9014  * Ext JS Library 1.1.1
9015  * Copyright(c) 2006-2007, Ext JS, LLC.
9016  *
9017  * Originally Released Under LGPL - original licence link has changed is not relivant.
9018  *
9019  * Fork - LGPL
9020  * <script type="text/javascript">
9021  */
9022
9023 /**
9024  * @class Roo.data.ArrayReader
9025  * @extends Roo.data.DataReader
9026  * Data reader class to create an Array of Roo.data.Record objects from an Array.
9027  * Each element of that Array represents a row of data fields. The
9028  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
9029  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
9030  * <p>
9031  * Example code:.
9032  * <pre><code>
9033 var RecordDef = Roo.data.Record.create([
9034     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
9035     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
9036 ]);
9037 var myReader = new Roo.data.ArrayReader({
9038     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
9039 }, RecordDef);
9040 </code></pre>
9041  * <p>
9042  * This would consume an Array like this:
9043  * <pre><code>
9044 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
9045   </code></pre>
9046  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
9047  * @constructor
9048  * Create a new JsonReader
9049  * @param {Object} meta Metadata configuration options.
9050  * @param {Object} recordType Either an Array of field definition objects
9051  * as specified to {@link Roo.data.Record#create},
9052  * or an {@link Roo.data.Record} object
9053  * created using {@link Roo.data.Record#create}.
9054  */
9055 Roo.data.ArrayReader = function(meta, recordType){
9056     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
9057 };
9058
9059 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
9060     /**
9061      * Create a data block containing Roo.data.Records from an XML document.
9062      * @param {Object} o An Array of row objects which represents the dataset.
9063      * @return {Object} data A data block which is used by an Roo.data.Store object as
9064      * a cache of Roo.data.Records.
9065      */
9066     readRecords : function(o){
9067         var sid = this.meta ? this.meta.id : null;
9068         var recordType = this.recordType, fields = recordType.prototype.fields;
9069         var records = [];
9070         var root = o;
9071             for(var i = 0; i < root.length; i++){
9072                     var n = root[i];
9073                 var values = {};
9074                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
9075                 for(var j = 0, jlen = fields.length; j < jlen; j++){
9076                 var f = fields.items[j];
9077                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
9078                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
9079                 v = f.convert(v);
9080                 values[f.name] = v;
9081             }
9082                 var record = new recordType(values, id);
9083                 record.json = n;
9084                 records[records.length] = record;
9085             }
9086             return {
9087                 records : records,
9088                 totalRecords : records.length
9089             };
9090     }
9091 });/*
9092  * - LGPL
9093  * * 
9094  */
9095
9096 /**
9097  * @class Roo.bootstrap.ComboBox
9098  * @extends Roo.bootstrap.TriggerField
9099  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
9100  * @cfg {Boolean} append (true|false) default false
9101  * @constructor
9102  * Create a new ComboBox.
9103  * @param {Object} config Configuration options
9104  */
9105 Roo.bootstrap.ComboBox = function(config){
9106     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
9107     this.addEvents({
9108         /**
9109          * @event expand
9110          * Fires when the dropdown list is expanded
9111              * @param {Roo.bootstrap.ComboBox} combo This combo box
9112              */
9113         'expand' : true,
9114         /**
9115          * @event collapse
9116          * Fires when the dropdown list is collapsed
9117              * @param {Roo.bootstrap.ComboBox} combo This combo box
9118              */
9119         'collapse' : true,
9120         /**
9121          * @event beforeselect
9122          * Fires before a list item is selected. Return false to cancel the selection.
9123              * @param {Roo.bootstrap.ComboBox} combo This combo box
9124              * @param {Roo.data.Record} record The data record returned from the underlying store
9125              * @param {Number} index The index of the selected item in the dropdown list
9126              */
9127         'beforeselect' : true,
9128         /**
9129          * @event select
9130          * Fires when a list item is selected
9131              * @param {Roo.bootstrap.ComboBox} combo This combo box
9132              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
9133              * @param {Number} index The index of the selected item in the dropdown list
9134              */
9135         'select' : true,
9136         /**
9137          * @event beforequery
9138          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
9139          * The event object passed has these properties:
9140              * @param {Roo.bootstrap.ComboBox} combo This combo box
9141              * @param {String} query The query
9142              * @param {Boolean} forceAll true to force "all" query
9143              * @param {Boolean} cancel true to cancel the query
9144              * @param {Object} e The query event object
9145              */
9146         'beforequery': true,
9147          /**
9148          * @event add
9149          * Fires when the 'add' icon is pressed (add a listener to enable add button)
9150              * @param {Roo.bootstrap.ComboBox} combo This combo box
9151              */
9152         'add' : true,
9153         /**
9154          * @event edit
9155          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
9156              * @param {Roo.bootstrap.ComboBox} combo This combo box
9157              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
9158              */
9159         'edit' : true,
9160         /**
9161          * @event remove
9162          * Fires when the remove value from the combobox array
9163              * @param {Roo.bootstrap.ComboBox} combo This combo box
9164              */
9165         'remove' : true
9166         
9167     });
9168     
9169     
9170     this.selectedIndex = -1;
9171     if(this.mode == 'local'){
9172         if(config.queryDelay === undefined){
9173             this.queryDelay = 10;
9174         }
9175         if(config.minChars === undefined){
9176             this.minChars = 0;
9177         }
9178     }
9179 };
9180
9181 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
9182      
9183     /**
9184      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
9185      * rendering into an Roo.Editor, defaults to false)
9186      */
9187     /**
9188      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
9189      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
9190      */
9191     /**
9192      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
9193      */
9194     /**
9195      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
9196      * the dropdown list (defaults to undefined, with no header element)
9197      */
9198
9199      /**
9200      * @cfg {String/Roo.Template} tpl The template to use to render the output
9201      */
9202      
9203      /**
9204      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
9205      */
9206     listWidth: undefined,
9207     /**
9208      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
9209      * mode = 'remote' or 'text' if mode = 'local')
9210      */
9211     displayField: undefined,
9212     /**
9213      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
9214      * mode = 'remote' or 'value' if mode = 'local'). 
9215      * Note: use of a valueField requires the user make a selection
9216      * in order for a value to be mapped.
9217      */
9218     valueField: undefined,
9219     
9220     
9221     /**
9222      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
9223      * field's data value (defaults to the underlying DOM element's name)
9224      */
9225     hiddenName: undefined,
9226     /**
9227      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
9228      */
9229     listClass: '',
9230     /**
9231      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
9232      */
9233     selectedClass: 'active',
9234     
9235     /**
9236      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
9237      */
9238     shadow:'sides',
9239     /**
9240      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
9241      * anchor positions (defaults to 'tl-bl')
9242      */
9243     listAlign: 'tl-bl?',
9244     /**
9245      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
9246      */
9247     maxHeight: 300,
9248     /**
9249      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
9250      * query specified by the allQuery config option (defaults to 'query')
9251      */
9252     triggerAction: 'query',
9253     /**
9254      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
9255      * (defaults to 4, does not apply if editable = false)
9256      */
9257     minChars : 4,
9258     /**
9259      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
9260      * delay (typeAheadDelay) if it matches a known value (defaults to false)
9261      */
9262     typeAhead: false,
9263     /**
9264      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
9265      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
9266      */
9267     queryDelay: 500,
9268     /**
9269      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
9270      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
9271      */
9272     pageSize: 0,
9273     /**
9274      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
9275      * when editable = true (defaults to false)
9276      */
9277     selectOnFocus:false,
9278     /**
9279      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
9280      */
9281     queryParam: 'query',
9282     /**
9283      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
9284      * when mode = 'remote' (defaults to 'Loading...')
9285      */
9286     loadingText: 'Loading...',
9287     /**
9288      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
9289      */
9290     resizable: false,
9291     /**
9292      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
9293      */
9294     handleHeight : 8,
9295     /**
9296      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
9297      * traditional select (defaults to true)
9298      */
9299     editable: true,
9300     /**
9301      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
9302      */
9303     allQuery: '',
9304     /**
9305      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
9306      */
9307     mode: 'remote',
9308     /**
9309      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
9310      * listWidth has a higher value)
9311      */
9312     minListWidth : 70,
9313     /**
9314      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
9315      * allow the user to set arbitrary text into the field (defaults to false)
9316      */
9317     forceSelection:false,
9318     /**
9319      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
9320      * if typeAhead = true (defaults to 250)
9321      */
9322     typeAheadDelay : 250,
9323     /**
9324      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
9325      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
9326      */
9327     valueNotFoundText : undefined,
9328     /**
9329      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
9330      */
9331     blockFocus : false,
9332     
9333     /**
9334      * @cfg {Boolean} disableClear Disable showing of clear button.
9335      */
9336     disableClear : false,
9337     /**
9338      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
9339      */
9340     alwaysQuery : false,
9341     
9342     /**
9343      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
9344      */
9345     multiple : false,
9346     
9347     //private
9348     addicon : false,
9349     editicon: false,
9350     
9351     page: 0,
9352     hasQuery: false,
9353     append: false,
9354     loadNext: false,
9355     item: [],
9356     
9357     // element that contains real text value.. (when hidden is used..)
9358      
9359     // private
9360     initEvents: function(){
9361         
9362         if (!this.store) {
9363             throw "can not find store for combo";
9364         }
9365         this.store = Roo.factory(this.store, Roo.data);
9366         
9367         
9368         
9369         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
9370         
9371         
9372         if(this.hiddenName){
9373             
9374             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
9375             
9376             this.hiddenField.dom.value =
9377                 this.hiddenValue !== undefined ? this.hiddenValue :
9378                 this.value !== undefined ? this.value : '';
9379
9380             // prevent input submission
9381             this.el.dom.removeAttribute('name');
9382             this.hiddenField.dom.setAttribute('name', this.hiddenName);
9383              
9384              
9385         }
9386         //if(Roo.isGecko){
9387         //    this.el.dom.setAttribute('autocomplete', 'off');
9388         //}
9389
9390         var cls = 'x-combo-list';
9391         this.list = this.el.select('ul.dropdown-menu',true).first();
9392
9393         //this.list = new Roo.Layer({
9394         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
9395         //});
9396         
9397         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
9398         this.list.setWidth(lw);
9399         
9400         this.list.on('mouseover', this.onViewOver, this);
9401         this.list.on('mousemove', this.onViewMove, this);
9402         
9403         this.list.on('scroll', this.onViewScroll, this);
9404         
9405         /*
9406         this.list.swallowEvent('mousewheel');
9407         this.assetHeight = 0;
9408
9409         if(this.title){
9410             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
9411             this.assetHeight += this.header.getHeight();
9412         }
9413
9414         this.innerList = this.list.createChild({cls:cls+'-inner'});
9415         this.innerList.on('mouseover', this.onViewOver, this);
9416         this.innerList.on('mousemove', this.onViewMove, this);
9417         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
9418         
9419         if(this.allowBlank && !this.pageSize && !this.disableClear){
9420             this.footer = this.list.createChild({cls:cls+'-ft'});
9421             this.pageTb = new Roo.Toolbar(this.footer);
9422            
9423         }
9424         if(this.pageSize){
9425             this.footer = this.list.createChild({cls:cls+'-ft'});
9426             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
9427                     {pageSize: this.pageSize});
9428             
9429         }
9430         
9431         if (this.pageTb && this.allowBlank && !this.disableClear) {
9432             var _this = this;
9433             this.pageTb.add(new Roo.Toolbar.Fill(), {
9434                 cls: 'x-btn-icon x-btn-clear',
9435                 text: '&#160;',
9436                 handler: function()
9437                 {
9438                     _this.collapse();
9439                     _this.clearValue();
9440                     _this.onSelect(false, -1);
9441                 }
9442             });
9443         }
9444         if (this.footer) {
9445             this.assetHeight += this.footer.getHeight();
9446         }
9447         */
9448             
9449         if(!this.tpl){
9450             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
9451         }
9452
9453         this.view = new Roo.View(this.el.select('ul.dropdown-menu',true).first(), this.tpl, {
9454             singleSelect:true, store: this.store, selectedClass: this.selectedClass
9455         });
9456         //this.view.wrapEl.setDisplayed(false);
9457         this.view.on('click', this.onViewClick, this);
9458         
9459         
9460         
9461         this.store.on('beforeload', this.onBeforeLoad, this);
9462         this.store.on('load', this.onLoad, this);
9463         this.store.on('loadexception', this.onLoadException, this);
9464         /*
9465         if(this.resizable){
9466             this.resizer = new Roo.Resizable(this.list,  {
9467                pinned:true, handles:'se'
9468             });
9469             this.resizer.on('resize', function(r, w, h){
9470                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
9471                 this.listWidth = w;
9472                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
9473                 this.restrictHeight();
9474             }, this);
9475             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
9476         }
9477         */
9478         if(!this.editable){
9479             this.editable = true;
9480             this.setEditable(false);
9481         }
9482         
9483         /*
9484         
9485         if (typeof(this.events.add.listeners) != 'undefined') {
9486             
9487             this.addicon = this.wrap.createChild(
9488                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
9489        
9490             this.addicon.on('click', function(e) {
9491                 this.fireEvent('add', this);
9492             }, this);
9493         }
9494         if (typeof(this.events.edit.listeners) != 'undefined') {
9495             
9496             this.editicon = this.wrap.createChild(
9497                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
9498             if (this.addicon) {
9499                 this.editicon.setStyle('margin-left', '40px');
9500             }
9501             this.editicon.on('click', function(e) {
9502                 
9503                 // we fire even  if inothing is selected..
9504                 this.fireEvent('edit', this, this.lastData );
9505                 
9506             }, this);
9507         }
9508         */
9509         
9510         this.keyNav = new Roo.KeyNav(this.inputEl(), {
9511             "up" : function(e){
9512                 this.inKeyMode = true;
9513                 this.selectPrev();
9514             },
9515
9516             "down" : function(e){
9517                 if(!this.isExpanded()){
9518                     this.onTriggerClick();
9519                 }else{
9520                     this.inKeyMode = true;
9521                     this.selectNext();
9522                 }
9523             },
9524
9525             "enter" : function(e){
9526                 this.onViewClick();
9527                 //return true;
9528             },
9529
9530             "esc" : function(e){
9531                 this.collapse();
9532             },
9533
9534             "tab" : function(e){
9535                 this.collapse();
9536                 
9537                 if(this.fireEvent("specialkey", this, e)){
9538                     this.onViewClick(false);
9539                 }
9540                 
9541                 return true;
9542             },
9543
9544             scope : this,
9545
9546             doRelay : function(foo, bar, hname){
9547                 if(hname == 'down' || this.scope.isExpanded()){
9548                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
9549                 }
9550                 return true;
9551             },
9552
9553             forceKeyDown: true
9554         });
9555         
9556         
9557         this.queryDelay = Math.max(this.queryDelay || 10,
9558                 this.mode == 'local' ? 10 : 250);
9559         
9560         
9561         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
9562         
9563         if(this.typeAhead){
9564             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
9565         }
9566         if(this.editable !== false){
9567             this.inputEl().on("keyup", this.onKeyUp, this);
9568         }
9569         if(this.forceSelection){
9570             this.inputEl().on('blur', this.doForce, this);
9571         }
9572         
9573         if(this.multiple){
9574             this.choices = this.el.select('ul.select2-choices', true).first();
9575             this.searchField = this.el.select('ul li.select2-search-field', true).first();
9576         }
9577     },
9578
9579     onDestroy : function(){
9580         if(this.view){
9581             this.view.setStore(null);
9582             this.view.el.removeAllListeners();
9583             this.view.el.remove();
9584             this.view.purgeListeners();
9585         }
9586         if(this.list){
9587             this.list.dom.innerHTML  = '';
9588         }
9589         if(this.store){
9590             this.store.un('beforeload', this.onBeforeLoad, this);
9591             this.store.un('load', this.onLoad, this);
9592             this.store.un('loadexception', this.onLoadException, this);
9593         }
9594         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
9595     },
9596
9597     // private
9598     fireKey : function(e){
9599         if(e.isNavKeyPress() && !this.list.isVisible()){
9600             this.fireEvent("specialkey", this, e);
9601         }
9602     },
9603
9604     // private
9605     onResize: function(w, h){
9606 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
9607 //        
9608 //        if(typeof w != 'number'){
9609 //            // we do not handle it!?!?
9610 //            return;
9611 //        }
9612 //        var tw = this.trigger.getWidth();
9613 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
9614 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
9615 //        var x = w - tw;
9616 //        this.inputEl().setWidth( this.adjustWidth('input', x));
9617 //            
9618 //        //this.trigger.setStyle('left', x+'px');
9619 //        
9620 //        if(this.list && this.listWidth === undefined){
9621 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
9622 //            this.list.setWidth(lw);
9623 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
9624 //        }
9625         
9626     
9627         
9628     },
9629
9630     /**
9631      * Allow or prevent the user from directly editing the field text.  If false is passed,
9632      * the user will only be able to select from the items defined in the dropdown list.  This method
9633      * is the runtime equivalent of setting the 'editable' config option at config time.
9634      * @param {Boolean} value True to allow the user to directly edit the field text
9635      */
9636     setEditable : function(value){
9637         if(value == this.editable){
9638             return;
9639         }
9640         this.editable = value;
9641         if(!value){
9642             this.inputEl().dom.setAttribute('readOnly', true);
9643             this.inputEl().on('mousedown', this.onTriggerClick,  this);
9644             this.inputEl().addClass('x-combo-noedit');
9645         }else{
9646             this.inputEl().dom.setAttribute('readOnly', false);
9647             this.inputEl().un('mousedown', this.onTriggerClick,  this);
9648             this.inputEl().removeClass('x-combo-noedit');
9649         }
9650     },
9651
9652     // private
9653     
9654     onBeforeLoad : function(combo,opts){
9655         if(!this.hasFocus){
9656             return;
9657         }
9658          if (!opts.add) {
9659             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
9660          }
9661         this.restrictHeight();
9662         this.selectedIndex = -1;
9663     },
9664
9665     // private
9666     onLoad : function(){
9667         
9668         this.hasQuery = false;
9669         
9670         if(!this.hasFocus){
9671             return;
9672         }
9673         
9674         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
9675             this.loading.hide();
9676         }
9677         
9678         if(this.store.getCount() > 0){
9679             this.expand();
9680             this.restrictHeight();
9681             if(this.lastQuery == this.allQuery){
9682                 if(this.editable){
9683                     this.inputEl().dom.select();
9684                 }
9685                 if(!this.selectByValue(this.value, true)){
9686                     this.select(0, true);
9687                 }
9688             }else{
9689                 this.selectNext();
9690                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
9691                     this.taTask.delay(this.typeAheadDelay);
9692                 }
9693             }
9694         }else{
9695             this.onEmptyResults();
9696         }
9697         
9698         //this.el.focus();
9699     },
9700     // private
9701     onLoadException : function()
9702     {
9703         this.hasQuery = false;
9704         
9705         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
9706             this.loading.hide();
9707         }
9708         
9709         this.collapse();
9710         Roo.log(this.store.reader.jsonData);
9711         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
9712             // fixme
9713             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
9714         }
9715         
9716         
9717     },
9718     // private
9719     onTypeAhead : function(){
9720         if(this.store.getCount() > 0){
9721             var r = this.store.getAt(0);
9722             var newValue = r.data[this.displayField];
9723             var len = newValue.length;
9724             var selStart = this.getRawValue().length;
9725             
9726             if(selStart != len){
9727                 this.setRawValue(newValue);
9728                 this.selectText(selStart, newValue.length);
9729             }
9730         }
9731     },
9732
9733     // private
9734     onSelect : function(record, index){
9735         
9736         if(this.fireEvent('beforeselect', this, record, index) !== false){
9737         
9738             this.setFromData(index > -1 ? record.data : false);
9739             
9740             this.collapse();
9741             this.fireEvent('select', this, record, index);
9742         }
9743     },
9744
9745     /**
9746      * Returns the currently selected field value or empty string if no value is set.
9747      * @return {String} value The selected value
9748      */
9749     getValue : function(){
9750         
9751         if(this.multiple){
9752             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
9753         }
9754         
9755         if(this.valueField){
9756             return typeof this.value != 'undefined' ? this.value : '';
9757         }else{
9758             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
9759         }
9760     },
9761
9762     /**
9763      * Clears any text/value currently set in the field
9764      */
9765     clearValue : function(){
9766         if(this.hiddenField){
9767             this.hiddenField.dom.value = '';
9768         }
9769         this.value = '';
9770         this.setRawValue('');
9771         this.lastSelectionText = '';
9772         
9773     },
9774
9775     /**
9776      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
9777      * will be displayed in the field.  If the value does not match the data value of an existing item,
9778      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
9779      * Otherwise the field will be blank (although the value will still be set).
9780      * @param {String} value The value to match
9781      */
9782     setValue : function(v){
9783         if(this.multiple){
9784             this.syncValue();
9785             return;
9786         }
9787         
9788         var text = v;
9789         if(this.valueField){
9790             var r = this.findRecord(this.valueField, v);
9791             if(r){
9792                 text = r.data[this.displayField];
9793             }else if(this.valueNotFoundText !== undefined){
9794                 text = this.valueNotFoundText;
9795             }
9796         }
9797         this.lastSelectionText = text;
9798         if(this.hiddenField){
9799             this.hiddenField.dom.value = v;
9800         }
9801         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
9802         this.value = v;
9803     },
9804     /**
9805      * @property {Object} the last set data for the element
9806      */
9807     
9808     lastData : false,
9809     /**
9810      * Sets the value of the field based on a object which is related to the record format for the store.
9811      * @param {Object} value the value to set as. or false on reset?
9812      */
9813     setFromData : function(o){
9814         
9815         if(this.multiple){
9816             this.addItem(o);
9817             return;
9818         }
9819             
9820         var dv = ''; // display value
9821         var vv = ''; // value value..
9822         this.lastData = o;
9823         if (this.displayField) {
9824             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
9825         } else {
9826             // this is an error condition!!!
9827             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
9828         }
9829         
9830         if(this.valueField){
9831             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
9832         }
9833         
9834         if(this.hiddenField){
9835             this.hiddenField.dom.value = vv;
9836             
9837             this.lastSelectionText = dv;
9838             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
9839             this.value = vv;
9840             return;
9841         }
9842         // no hidden field.. - we store the value in 'value', but still display
9843         // display field!!!!
9844         this.lastSelectionText = dv;
9845         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
9846         this.value = vv;
9847         
9848         
9849     },
9850     // private
9851     reset : function(){
9852         // overridden so that last data is reset..
9853         this.setValue(this.originalValue);
9854         this.clearInvalid();
9855         this.lastData = false;
9856         if (this.view) {
9857             this.view.clearSelections();
9858         }
9859     },
9860     // private
9861     findRecord : function(prop, value){
9862         var record;
9863         if(this.store.getCount() > 0){
9864             this.store.each(function(r){
9865                 if(r.data[prop] == value){
9866                     record = r;
9867                     return false;
9868                 }
9869                 return true;
9870             });
9871         }
9872         return record;
9873     },
9874     
9875     getName: function()
9876     {
9877         // returns hidden if it's set..
9878         if (!this.rendered) {return ''};
9879         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
9880         
9881     },
9882     // private
9883     onViewMove : function(e, t){
9884         this.inKeyMode = false;
9885     },
9886
9887     // private
9888     onViewOver : function(e, t){
9889         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
9890             return;
9891         }
9892         var item = this.view.findItemFromChild(t);
9893         if(item){
9894             var index = this.view.indexOf(item);
9895             this.select(index, false);
9896         }
9897     },
9898
9899     // private
9900     onViewClick : function(doFocus)
9901     {
9902         var index = this.view.getSelectedIndexes()[0];
9903         var r = this.store.getAt(index);
9904         if(r){
9905             this.onSelect(r, index);
9906         }
9907         if(doFocus !== false && !this.blockFocus){
9908             this.inputEl().focus();
9909         }
9910     },
9911
9912     // private
9913     restrictHeight : function(){
9914         //this.innerList.dom.style.height = '';
9915         //var inner = this.innerList.dom;
9916         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
9917         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
9918         //this.list.beginUpdate();
9919         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
9920         this.list.alignTo(this.inputEl(), this.listAlign);
9921         //this.list.endUpdate();
9922     },
9923
9924     // private
9925     onEmptyResults : function(){
9926         this.collapse();
9927     },
9928
9929     /**
9930      * Returns true if the dropdown list is expanded, else false.
9931      */
9932     isExpanded : function(){
9933         return this.list.isVisible();
9934     },
9935
9936     /**
9937      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
9938      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
9939      * @param {String} value The data value of the item to select
9940      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
9941      * selected item if it is not currently in view (defaults to true)
9942      * @return {Boolean} True if the value matched an item in the list, else false
9943      */
9944     selectByValue : function(v, scrollIntoView){
9945         if(v !== undefined && v !== null){
9946             var r = this.findRecord(this.valueField || this.displayField, v);
9947             if(r){
9948                 this.select(this.store.indexOf(r), scrollIntoView);
9949                 return true;
9950             }
9951         }
9952         return false;
9953     },
9954
9955     /**
9956      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
9957      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
9958      * @param {Number} index The zero-based index of the list item to select
9959      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
9960      * selected item if it is not currently in view (defaults to true)
9961      */
9962     select : function(index, scrollIntoView){
9963         this.selectedIndex = index;
9964         this.view.select(index);
9965         if(scrollIntoView !== false){
9966             var el = this.view.getNode(index);
9967             if(el){
9968                 //this.innerList.scrollChildIntoView(el, false);
9969                 
9970             }
9971         }
9972     },
9973
9974     // private
9975     selectNext : function(){
9976         var ct = this.store.getCount();
9977         if(ct > 0){
9978             if(this.selectedIndex == -1){
9979                 this.select(0);
9980             }else if(this.selectedIndex < ct-1){
9981                 this.select(this.selectedIndex+1);
9982             }
9983         }
9984     },
9985
9986     // private
9987     selectPrev : function(){
9988         var ct = this.store.getCount();
9989         if(ct > 0){
9990             if(this.selectedIndex == -1){
9991                 this.select(0);
9992             }else if(this.selectedIndex != 0){
9993                 this.select(this.selectedIndex-1);
9994             }
9995         }
9996     },
9997
9998     // private
9999     onKeyUp : function(e){
10000         if(this.editable !== false && !e.isSpecialKey()){
10001             this.lastKey = e.getKey();
10002             this.dqTask.delay(this.queryDelay);
10003         }
10004     },
10005
10006     // private
10007     validateBlur : function(){
10008         return !this.list || !this.list.isVisible();   
10009     },
10010
10011     // private
10012     initQuery : function(){
10013         this.doQuery(this.getRawValue());
10014     },
10015
10016     // private
10017     doForce : function(){
10018         if(this.inputEl().dom.value.length > 0){
10019             this.inputEl().dom.value =
10020                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
10021              
10022         }
10023     },
10024
10025     /**
10026      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
10027      * query allowing the query action to be canceled if needed.
10028      * @param {String} query The SQL query to execute
10029      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
10030      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
10031      * saved in the current store (defaults to false)
10032      */
10033     doQuery : function(q, forceAll){
10034         
10035         if(q === undefined || q === null){
10036             q = '';
10037         }
10038         var qe = {
10039             query: q,
10040             forceAll: forceAll,
10041             combo: this,
10042             cancel:false
10043         };
10044         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
10045             return false;
10046         }
10047         q = qe.query;
10048         
10049         forceAll = qe.forceAll;
10050         if(forceAll === true || (q.length >= this.minChars)){
10051             
10052             this.hasQuery = true;
10053             
10054             if(this.lastQuery != q || this.alwaysQuery){
10055                 this.lastQuery = q;
10056                 if(this.mode == 'local'){
10057                     this.selectedIndex = -1;
10058                     if(forceAll){
10059                         this.store.clearFilter();
10060                     }else{
10061                         this.store.filter(this.displayField, q);
10062                     }
10063                     this.onLoad();
10064                 }else{
10065                     this.store.baseParams[this.queryParam] = q;
10066                     
10067                     var options = {params : this.getParams(q)};
10068                     
10069                     if(this.loadNext){
10070                         options.add = true;
10071                         options.params.start = this.page * this.pageSize;
10072                     }
10073                     
10074                     this.store.load(options);
10075                     this.expand();
10076                 }
10077             }else{
10078                 this.selectedIndex = -1;
10079                 this.onLoad();   
10080             }
10081         }
10082         
10083         this.loadNext = false;
10084     },
10085
10086     // private
10087     getParams : function(q){
10088         var p = {};
10089         //p[this.queryParam] = q;
10090         
10091         if(this.pageSize){
10092             p.start = 0;
10093             p.limit = this.pageSize;
10094         }
10095         return p;
10096     },
10097
10098     /**
10099      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
10100      */
10101     collapse : function(){
10102         if(!this.isExpanded()){
10103             return;
10104         }
10105         
10106         this.list.hide();
10107         Roo.get(document).un('mousedown', this.collapseIf, this);
10108         Roo.get(document).un('mousewheel', this.collapseIf, this);
10109         if (!this.editable) {
10110             Roo.get(document).un('keydown', this.listKeyPress, this);
10111         }
10112         this.fireEvent('collapse', this);
10113     },
10114
10115     // private
10116     collapseIf : function(e){
10117         var in_combo  = e.within(this.el);
10118         var in_list =  e.within(this.list);
10119         
10120         if (in_combo || in_list) {
10121             //e.stopPropagation();
10122             return;
10123         }
10124
10125         this.collapse();
10126         
10127     },
10128
10129     /**
10130      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
10131      */
10132     expand : function(){
10133        
10134         if(this.isExpanded() || !this.hasFocus){
10135             return;
10136         }
10137          Roo.log('expand');
10138         this.list.alignTo(this.inputEl(), this.listAlign);
10139         this.list.show();
10140         Roo.get(document).on('mousedown', this.collapseIf, this);
10141         Roo.get(document).on('mousewheel', this.collapseIf, this);
10142         if (!this.editable) {
10143             Roo.get(document).on('keydown', this.listKeyPress, this);
10144         }
10145         
10146         this.fireEvent('expand', this);
10147     },
10148
10149     // private
10150     // Implements the default empty TriggerField.onTriggerClick function
10151     onTriggerClick : function()
10152     {
10153         Roo.log('trigger click');
10154         
10155         if(this.disabled){
10156             return;
10157         }
10158         
10159         this.page = 0;
10160         this.loadNext = false;
10161         
10162         if(this.isExpanded()){
10163             this.collapse();
10164             if (!this.blockFocus) {
10165                 this.inputEl().focus();
10166             }
10167             
10168         }else {
10169             this.hasFocus = true;
10170             if(this.triggerAction == 'all') {
10171                 this.doQuery(this.allQuery, true);
10172             } else {
10173                 this.doQuery(this.getRawValue());
10174             }
10175             if (!this.blockFocus) {
10176                 this.inputEl().focus();
10177             }
10178         }
10179     },
10180     listKeyPress : function(e)
10181     {
10182         //Roo.log('listkeypress');
10183         // scroll to first matching element based on key pres..
10184         if (e.isSpecialKey()) {
10185             return false;
10186         }
10187         var k = String.fromCharCode(e.getKey()).toUpperCase();
10188         //Roo.log(k);
10189         var match  = false;
10190         var csel = this.view.getSelectedNodes();
10191         var cselitem = false;
10192         if (csel.length) {
10193             var ix = this.view.indexOf(csel[0]);
10194             cselitem  = this.store.getAt(ix);
10195             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
10196                 cselitem = false;
10197             }
10198             
10199         }
10200         
10201         this.store.each(function(v) { 
10202             if (cselitem) {
10203                 // start at existing selection.
10204                 if (cselitem.id == v.id) {
10205                     cselitem = false;
10206                 }
10207                 return true;
10208             }
10209                 
10210             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
10211                 match = this.store.indexOf(v);
10212                 return false;
10213             }
10214             return true;
10215         }, this);
10216         
10217         if (match === false) {
10218             return true; // no more action?
10219         }
10220         // scroll to?
10221         this.view.select(match);
10222         var sn = Roo.get(this.view.getSelectedNodes()[0])
10223         //sn.scrollIntoView(sn.dom.parentNode, false);
10224     },
10225     
10226     onViewScroll : function(e, t){
10227         
10228         if(this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
10229             return;
10230         }
10231         
10232         this.hasQuery = true;
10233         
10234         this.loading = this.list.select('.loading', true).first();
10235         
10236         if(this.loading === null){
10237             this.list.createChild({
10238                 tag: 'div',
10239                 cls: 'loading select2-more-results select2-active',
10240                 html: 'Loading more results...'
10241             })
10242             
10243             this.loading = this.list.select('.loading', true).first();
10244             
10245             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
10246             
10247             this.loading.hide();
10248         }
10249         
10250         this.loading.show();
10251         
10252         var _combo = this;
10253         
10254         this.page++;
10255         this.loadNext = true;
10256         
10257         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
10258         
10259         return;
10260     },
10261     
10262     addItem : function(o)
10263     {   
10264         var dv = ''; // display value
10265         
10266         if (this.displayField) {
10267             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
10268         } else {
10269             // this is an error condition!!!
10270             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
10271         }
10272         
10273         if(!dv.length){
10274             return;
10275         }
10276         
10277         var choice = this.choices.createChild({
10278             tag: 'li',
10279             cls: 'select2-search-choice',
10280             cn: [
10281                 {
10282                     tag: 'div',
10283                     html: dv
10284                 },
10285                 {
10286                     tag: 'a',
10287                     href: '#',
10288                     cls: 'select2-search-choice-close',
10289                     tabindex: '-1'
10290                 }
10291             ]
10292             
10293         }, this.searchField);
10294         
10295         var close = choice.select('a.select2-search-choice-close', true).first()
10296         
10297         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
10298         
10299         this.item.push(o);
10300         this.lastData = o;
10301         
10302         this.syncValue();
10303         
10304         this.inputEl().dom.value = '';
10305         
10306     },
10307     
10308     onRemoveItem : function(e, _self, o)
10309     {
10310         e.preventDefault();
10311         var index = this.item.indexOf(o.data) * 1;
10312         
10313         if( index < 0){
10314             Roo.log('not this item?!');
10315             return;
10316         }
10317         
10318         this.item.splice(index, 1);
10319         o.item.remove();
10320         
10321         this.syncValue();
10322         
10323         this.fireEvent('remove', this, e);
10324         
10325     },
10326     
10327     syncValue : function()
10328     {
10329         if(!this.item.length){
10330             this.clearValue();
10331             return;
10332         }
10333             
10334         var value = [];
10335         var _this = this;
10336         Roo.each(this.item, function(i){
10337             if(_this.valueField){
10338                 value.push(i[_this.valueField]);
10339                 return;
10340             }
10341
10342             value.push(i);
10343         });
10344
10345         this.value = value.join(',');
10346
10347         if(this.hiddenField){
10348             this.hiddenField.dom.value = this.value;
10349         }
10350     },
10351     
10352     clearItem : function()
10353     {
10354         if(!this.multiple){
10355             return;
10356         }
10357         
10358         this.item = [];
10359         
10360         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
10361            c.remove();
10362         });
10363         
10364         this.syncValue();
10365     }
10366     
10367     
10368
10369     /** 
10370     * @cfg {Boolean} grow 
10371     * @hide 
10372     */
10373     /** 
10374     * @cfg {Number} growMin 
10375     * @hide 
10376     */
10377     /** 
10378     * @cfg {Number} growMax 
10379     * @hide 
10380     */
10381     /**
10382      * @hide
10383      * @method autoSize
10384      */
10385 });
10386 /*
10387  * Based on:
10388  * Ext JS Library 1.1.1
10389  * Copyright(c) 2006-2007, Ext JS, LLC.
10390  *
10391  * Originally Released Under LGPL - original licence link has changed is not relivant.
10392  *
10393  * Fork - LGPL
10394  * <script type="text/javascript">
10395  */
10396
10397 /**
10398  * @class Roo.View
10399  * @extends Roo.util.Observable
10400  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
10401  * This class also supports single and multi selection modes. <br>
10402  * Create a data model bound view:
10403  <pre><code>
10404  var store = new Roo.data.Store(...);
10405
10406  var view = new Roo.View({
10407     el : "my-element",
10408     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
10409  
10410     singleSelect: true,
10411     selectedClass: "ydataview-selected",
10412     store: store
10413  });
10414
10415  // listen for node click?
10416  view.on("click", function(vw, index, node, e){
10417  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
10418  });
10419
10420  // load XML data
10421  dataModel.load("foobar.xml");
10422  </code></pre>
10423  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
10424  * <br><br>
10425  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
10426  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
10427  * 
10428  * Note: old style constructor is still suported (container, template, config)
10429  * 
10430  * @constructor
10431  * Create a new View
10432  * @param {Object} config The config object
10433  * 
10434  */
10435 Roo.View = function(config, depreciated_tpl, depreciated_config){
10436     
10437     if (typeof(depreciated_tpl) == 'undefined') {
10438         // new way.. - universal constructor.
10439         Roo.apply(this, config);
10440         this.el  = Roo.get(this.el);
10441     } else {
10442         // old format..
10443         this.el  = Roo.get(config);
10444         this.tpl = depreciated_tpl;
10445         Roo.apply(this, depreciated_config);
10446     }
10447     this.wrapEl  = this.el.wrap().wrap();
10448     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
10449     
10450     
10451     if(typeof(this.tpl) == "string"){
10452         this.tpl = new Roo.Template(this.tpl);
10453     } else {
10454         // support xtype ctors..
10455         this.tpl = new Roo.factory(this.tpl, Roo);
10456     }
10457     
10458     
10459     this.tpl.compile();
10460    
10461   
10462     
10463      
10464     /** @private */
10465     this.addEvents({
10466         /**
10467          * @event beforeclick
10468          * Fires before a click is processed. Returns false to cancel the default action.
10469          * @param {Roo.View} this
10470          * @param {Number} index The index of the target node
10471          * @param {HTMLElement} node The target node
10472          * @param {Roo.EventObject} e The raw event object
10473          */
10474             "beforeclick" : true,
10475         /**
10476          * @event click
10477          * Fires when a template node is clicked.
10478          * @param {Roo.View} this
10479          * @param {Number} index The index of the target node
10480          * @param {HTMLElement} node The target node
10481          * @param {Roo.EventObject} e The raw event object
10482          */
10483             "click" : true,
10484         /**
10485          * @event dblclick
10486          * Fires when a template node is double clicked.
10487          * @param {Roo.View} this
10488          * @param {Number} index The index of the target node
10489          * @param {HTMLElement} node The target node
10490          * @param {Roo.EventObject} e The raw event object
10491          */
10492             "dblclick" : true,
10493         /**
10494          * @event contextmenu
10495          * Fires when a template node is right clicked.
10496          * @param {Roo.View} this
10497          * @param {Number} index The index of the target node
10498          * @param {HTMLElement} node The target node
10499          * @param {Roo.EventObject} e The raw event object
10500          */
10501             "contextmenu" : true,
10502         /**
10503          * @event selectionchange
10504          * Fires when the selected nodes change.
10505          * @param {Roo.View} this
10506          * @param {Array} selections Array of the selected nodes
10507          */
10508             "selectionchange" : true,
10509     
10510         /**
10511          * @event beforeselect
10512          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
10513          * @param {Roo.View} this
10514          * @param {HTMLElement} node The node to be selected
10515          * @param {Array} selections Array of currently selected nodes
10516          */
10517             "beforeselect" : true,
10518         /**
10519          * @event preparedata
10520          * Fires on every row to render, to allow you to change the data.
10521          * @param {Roo.View} this
10522          * @param {Object} data to be rendered (change this)
10523          */
10524           "preparedata" : true
10525           
10526           
10527         });
10528
10529
10530
10531     this.el.on({
10532         "click": this.onClick,
10533         "dblclick": this.onDblClick,
10534         "contextmenu": this.onContextMenu,
10535         scope:this
10536     });
10537
10538     this.selections = [];
10539     this.nodes = [];
10540     this.cmp = new Roo.CompositeElementLite([]);
10541     if(this.store){
10542         this.store = Roo.factory(this.store, Roo.data);
10543         this.setStore(this.store, true);
10544     }
10545     
10546     if ( this.footer && this.footer.xtype) {
10547            
10548          var fctr = this.wrapEl.appendChild(document.createElement("div"));
10549         
10550         this.footer.dataSource = this.store
10551         this.footer.container = fctr;
10552         this.footer = Roo.factory(this.footer, Roo);
10553         fctr.insertFirst(this.el);
10554         
10555         // this is a bit insane - as the paging toolbar seems to detach the el..
10556 //        dom.parentNode.parentNode.parentNode
10557          // they get detached?
10558     }
10559     
10560     
10561     Roo.View.superclass.constructor.call(this);
10562     
10563     
10564 };
10565
10566 Roo.extend(Roo.View, Roo.util.Observable, {
10567     
10568      /**
10569      * @cfg {Roo.data.Store} store Data store to load data from.
10570      */
10571     store : false,
10572     
10573     /**
10574      * @cfg {String|Roo.Element} el The container element.
10575      */
10576     el : '',
10577     
10578     /**
10579      * @cfg {String|Roo.Template} tpl The template used by this View 
10580      */
10581     tpl : false,
10582     /**
10583      * @cfg {String} dataName the named area of the template to use as the data area
10584      *                          Works with domtemplates roo-name="name"
10585      */
10586     dataName: false,
10587     /**
10588      * @cfg {String} selectedClass The css class to add to selected nodes
10589      */
10590     selectedClass : "x-view-selected",
10591      /**
10592      * @cfg {String} emptyText The empty text to show when nothing is loaded.
10593      */
10594     emptyText : "",
10595     
10596     /**
10597      * @cfg {String} text to display on mask (default Loading)
10598      */
10599     mask : false,
10600     /**
10601      * @cfg {Boolean} multiSelect Allow multiple selection
10602      */
10603     multiSelect : false,
10604     /**
10605      * @cfg {Boolean} singleSelect Allow single selection
10606      */
10607     singleSelect:  false,
10608     
10609     /**
10610      * @cfg {Boolean} toggleSelect - selecting 
10611      */
10612     toggleSelect : false,
10613     
10614     /**
10615      * Returns the element this view is bound to.
10616      * @return {Roo.Element}
10617      */
10618     getEl : function(){
10619         return this.wrapEl;
10620     },
10621     
10622     
10623
10624     /**
10625      * Refreshes the view. - called by datachanged on the store. - do not call directly.
10626      */
10627     refresh : function(){
10628         Roo.log('refresh');
10629         var t = this.tpl;
10630         
10631         // if we are using something like 'domtemplate', then
10632         // the what gets used is:
10633         // t.applySubtemplate(NAME, data, wrapping data..)
10634         // the outer template then get' applied with
10635         //     the store 'extra data'
10636         // and the body get's added to the
10637         //      roo-name="data" node?
10638         //      <span class='roo-tpl-{name}'></span> ?????
10639         
10640         
10641         
10642         this.clearSelections();
10643         this.el.update("");
10644         var html = [];
10645         var records = this.store.getRange();
10646         if(records.length < 1) {
10647             
10648             // is this valid??  = should it render a template??
10649             
10650             this.el.update(this.emptyText);
10651             return;
10652         }
10653         var el = this.el;
10654         if (this.dataName) {
10655             this.el.update(t.apply(this.store.meta)); //????
10656             el = this.el.child('.roo-tpl-' + this.dataName);
10657         }
10658         
10659         for(var i = 0, len = records.length; i < len; i++){
10660             var data = this.prepareData(records[i].data, i, records[i]);
10661             this.fireEvent("preparedata", this, data, i, records[i]);
10662             html[html.length] = Roo.util.Format.trim(
10663                 this.dataName ?
10664                     t.applySubtemplate(this.dataName, data, this.store.meta) :
10665                     t.apply(data)
10666             );
10667         }
10668         
10669         
10670         
10671         el.update(html.join(""));
10672         this.nodes = el.dom.childNodes;
10673         this.updateIndexes(0);
10674     },
10675     
10676
10677     /**
10678      * Function to override to reformat the data that is sent to
10679      * the template for each node.
10680      * DEPRICATED - use the preparedata event handler.
10681      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
10682      * a JSON object for an UpdateManager bound view).
10683      */
10684     prepareData : function(data, index, record)
10685     {
10686         this.fireEvent("preparedata", this, data, index, record);
10687         return data;
10688     },
10689
10690     onUpdate : function(ds, record){
10691          Roo.log('on update');   
10692         this.clearSelections();
10693         var index = this.store.indexOf(record);
10694         var n = this.nodes[index];
10695         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
10696         n.parentNode.removeChild(n);
10697         this.updateIndexes(index, index);
10698     },
10699
10700     
10701     
10702 // --------- FIXME     
10703     onAdd : function(ds, records, index)
10704     {
10705         Roo.log(['on Add', ds, records, index] );        
10706         this.clearSelections();
10707         if(this.nodes.length == 0){
10708             this.refresh();
10709             return;
10710         }
10711         var n = this.nodes[index];
10712         for(var i = 0, len = records.length; i < len; i++){
10713             var d = this.prepareData(records[i].data, i, records[i]);
10714             if(n){
10715                 this.tpl.insertBefore(n, d);
10716             }else{
10717                 
10718                 this.tpl.append(this.el, d);
10719             }
10720         }
10721         this.updateIndexes(index);
10722     },
10723
10724     onRemove : function(ds, record, index){
10725         Roo.log('onRemove');
10726         this.clearSelections();
10727         var el = this.dataName  ?
10728             this.el.child('.roo-tpl-' + this.dataName) :
10729             this.el; 
10730         
10731         el.dom.removeChild(this.nodes[index]);
10732         this.updateIndexes(index);
10733     },
10734
10735     /**
10736      * Refresh an individual node.
10737      * @param {Number} index
10738      */
10739     refreshNode : function(index){
10740         this.onUpdate(this.store, this.store.getAt(index));
10741     },
10742
10743     updateIndexes : function(startIndex, endIndex){
10744         var ns = this.nodes;
10745         startIndex = startIndex || 0;
10746         endIndex = endIndex || ns.length - 1;
10747         for(var i = startIndex; i <= endIndex; i++){
10748             ns[i].nodeIndex = i;
10749         }
10750     },
10751
10752     /**
10753      * Changes the data store this view uses and refresh the view.
10754      * @param {Store} store
10755      */
10756     setStore : function(store, initial){
10757         if(!initial && this.store){
10758             this.store.un("datachanged", this.refresh);
10759             this.store.un("add", this.onAdd);
10760             this.store.un("remove", this.onRemove);
10761             this.store.un("update", this.onUpdate);
10762             this.store.un("clear", this.refresh);
10763             this.store.un("beforeload", this.onBeforeLoad);
10764             this.store.un("load", this.onLoad);
10765             this.store.un("loadexception", this.onLoad);
10766         }
10767         if(store){
10768           
10769             store.on("datachanged", this.refresh, this);
10770             store.on("add", this.onAdd, this);
10771             store.on("remove", this.onRemove, this);
10772             store.on("update", this.onUpdate, this);
10773             store.on("clear", this.refresh, this);
10774             store.on("beforeload", this.onBeforeLoad, this);
10775             store.on("load", this.onLoad, this);
10776             store.on("loadexception", this.onLoad, this);
10777         }
10778         
10779         if(store){
10780             this.refresh();
10781         }
10782     },
10783     /**
10784      * onbeforeLoad - masks the loading area.
10785      *
10786      */
10787     onBeforeLoad : function(store,opts)
10788     {
10789          Roo.log('onBeforeLoad');   
10790         if (!opts.add) {
10791             this.el.update("");
10792         }
10793         this.el.mask(this.mask ? this.mask : "Loading" ); 
10794     },
10795     onLoad : function ()
10796     {
10797         this.el.unmask();
10798     },
10799     
10800
10801     /**
10802      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
10803      * @param {HTMLElement} node
10804      * @return {HTMLElement} The template node
10805      */
10806     findItemFromChild : function(node){
10807         var el = this.dataName  ?
10808             this.el.child('.roo-tpl-' + this.dataName,true) :
10809             this.el.dom; 
10810         
10811         if(!node || node.parentNode == el){
10812                     return node;
10813             }
10814             var p = node.parentNode;
10815             while(p && p != el){
10816             if(p.parentNode == el){
10817                 return p;
10818             }
10819             p = p.parentNode;
10820         }
10821             return null;
10822     },
10823
10824     /** @ignore */
10825     onClick : function(e){
10826         var item = this.findItemFromChild(e.getTarget());
10827         if(item){
10828             var index = this.indexOf(item);
10829             if(this.onItemClick(item, index, e) !== false){
10830                 this.fireEvent("click", this, index, item, e);
10831             }
10832         }else{
10833             this.clearSelections();
10834         }
10835     },
10836
10837     /** @ignore */
10838     onContextMenu : function(e){
10839         var item = this.findItemFromChild(e.getTarget());
10840         if(item){
10841             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
10842         }
10843     },
10844
10845     /** @ignore */
10846     onDblClick : function(e){
10847         var item = this.findItemFromChild(e.getTarget());
10848         if(item){
10849             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
10850         }
10851     },
10852
10853     onItemClick : function(item, index, e)
10854     {
10855         if(this.fireEvent("beforeclick", this, index, item, e) === false){
10856             return false;
10857         }
10858         if (this.toggleSelect) {
10859             var m = this.isSelected(item) ? 'unselect' : 'select';
10860             Roo.log(m);
10861             var _t = this;
10862             _t[m](item, true, false);
10863             return true;
10864         }
10865         if(this.multiSelect || this.singleSelect){
10866             if(this.multiSelect && e.shiftKey && this.lastSelection){
10867                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
10868             }else{
10869                 this.select(item, this.multiSelect && e.ctrlKey);
10870                 this.lastSelection = item;
10871             }
10872             e.preventDefault();
10873         }
10874         return true;
10875     },
10876
10877     /**
10878      * Get the number of selected nodes.
10879      * @return {Number}
10880      */
10881     getSelectionCount : function(){
10882         return this.selections.length;
10883     },
10884
10885     /**
10886      * Get the currently selected nodes.
10887      * @return {Array} An array of HTMLElements
10888      */
10889     getSelectedNodes : function(){
10890         return this.selections;
10891     },
10892
10893     /**
10894      * Get the indexes of the selected nodes.
10895      * @return {Array}
10896      */
10897     getSelectedIndexes : function(){
10898         var indexes = [], s = this.selections;
10899         for(var i = 0, len = s.length; i < len; i++){
10900             indexes.push(s[i].nodeIndex);
10901         }
10902         return indexes;
10903     },
10904
10905     /**
10906      * Clear all selections
10907      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
10908      */
10909     clearSelections : function(suppressEvent){
10910         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
10911             this.cmp.elements = this.selections;
10912             this.cmp.removeClass(this.selectedClass);
10913             this.selections = [];
10914             if(!suppressEvent){
10915                 this.fireEvent("selectionchange", this, this.selections);
10916             }
10917         }
10918     },
10919
10920     /**
10921      * Returns true if the passed node is selected
10922      * @param {HTMLElement/Number} node The node or node index
10923      * @return {Boolean}
10924      */
10925     isSelected : function(node){
10926         var s = this.selections;
10927         if(s.length < 1){
10928             return false;
10929         }
10930         node = this.getNode(node);
10931         return s.indexOf(node) !== -1;
10932     },
10933
10934     /**
10935      * Selects nodes.
10936      * @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
10937      * @param {Boolean} keepExisting (optional) true to keep existing selections
10938      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
10939      */
10940     select : function(nodeInfo, keepExisting, suppressEvent){
10941         if(nodeInfo instanceof Array){
10942             if(!keepExisting){
10943                 this.clearSelections(true);
10944             }
10945             for(var i = 0, len = nodeInfo.length; i < len; i++){
10946                 this.select(nodeInfo[i], true, true);
10947             }
10948             return;
10949         } 
10950         var node = this.getNode(nodeInfo);
10951         if(!node || this.isSelected(node)){
10952             return; // already selected.
10953         }
10954         if(!keepExisting){
10955             this.clearSelections(true);
10956         }
10957         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
10958             Roo.fly(node).addClass(this.selectedClass);
10959             this.selections.push(node);
10960             if(!suppressEvent){
10961                 this.fireEvent("selectionchange", this, this.selections);
10962             }
10963         }
10964         
10965         
10966     },
10967       /**
10968      * Unselects nodes.
10969      * @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
10970      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
10971      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
10972      */
10973     unselect : function(nodeInfo, keepExisting, suppressEvent)
10974     {
10975         if(nodeInfo instanceof Array){
10976             Roo.each(this.selections, function(s) {
10977                 this.unselect(s, nodeInfo);
10978             }, this);
10979             return;
10980         }
10981         var node = this.getNode(nodeInfo);
10982         if(!node || !this.isSelected(node)){
10983             Roo.log("not selected");
10984             return; // not selected.
10985         }
10986         // fireevent???
10987         var ns = [];
10988         Roo.each(this.selections, function(s) {
10989             if (s == node ) {
10990                 Roo.fly(node).removeClass(this.selectedClass);
10991
10992                 return;
10993             }
10994             ns.push(s);
10995         },this);
10996         
10997         this.selections= ns;
10998         this.fireEvent("selectionchange", this, this.selections);
10999     },
11000
11001     /**
11002      * Gets a template node.
11003      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
11004      * @return {HTMLElement} The node or null if it wasn't found
11005      */
11006     getNode : function(nodeInfo){
11007         if(typeof nodeInfo == "string"){
11008             return document.getElementById(nodeInfo);
11009         }else if(typeof nodeInfo == "number"){
11010             return this.nodes[nodeInfo];
11011         }
11012         return nodeInfo;
11013     },
11014
11015     /**
11016      * Gets a range template nodes.
11017      * @param {Number} startIndex
11018      * @param {Number} endIndex
11019      * @return {Array} An array of nodes
11020      */
11021     getNodes : function(start, end){
11022         var ns = this.nodes;
11023         start = start || 0;
11024         end = typeof end == "undefined" ? ns.length - 1 : end;
11025         var nodes = [];
11026         if(start <= end){
11027             for(var i = start; i <= end; i++){
11028                 nodes.push(ns[i]);
11029             }
11030         } else{
11031             for(var i = start; i >= end; i--){
11032                 nodes.push(ns[i]);
11033             }
11034         }
11035         return nodes;
11036     },
11037
11038     /**
11039      * Finds the index of the passed node
11040      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
11041      * @return {Number} The index of the node or -1
11042      */
11043     indexOf : function(node){
11044         node = this.getNode(node);
11045         if(typeof node.nodeIndex == "number"){
11046             return node.nodeIndex;
11047         }
11048         var ns = this.nodes;
11049         for(var i = 0, len = ns.length; i < len; i++){
11050             if(ns[i] == node){
11051                 return i;
11052             }
11053         }
11054         return -1;
11055     }
11056 });
11057 /*
11058  * - LGPL
11059  *
11060  * based on jquery fullcalendar
11061  * 
11062  */
11063
11064 Roo.bootstrap = Roo.bootstrap || {};
11065 /**
11066  * @class Roo.bootstrap.Calendar
11067  * @extends Roo.bootstrap.Component
11068  * Bootstrap Calendar class
11069  * @cfg {Boolean} loadMask (true|false) default false
11070  * @cfg {Object} header generate the user specific header of the calendar, default false
11071
11072  * @constructor
11073  * Create a new Container
11074  * @param {Object} config The config object
11075  */
11076
11077
11078
11079 Roo.bootstrap.Calendar = function(config){
11080     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
11081      this.addEvents({
11082         /**
11083              * @event select
11084              * Fires when a date is selected
11085              * @param {DatePicker} this
11086              * @param {Date} date The selected date
11087              */
11088         'select': true,
11089         /**
11090              * @event monthchange
11091              * Fires when the displayed month changes 
11092              * @param {DatePicker} this
11093              * @param {Date} date The selected month
11094              */
11095         'monthchange': true,
11096         /**
11097              * @event evententer
11098              * Fires when mouse over an event
11099              * @param {Calendar} this
11100              * @param {event} Event
11101              */
11102         'evententer': true,
11103         /**
11104              * @event eventleave
11105              * Fires when the mouse leaves an
11106              * @param {Calendar} this
11107              * @param {event}
11108              */
11109         'eventleave': true,
11110         /**
11111              * @event eventclick
11112              * Fires when the mouse click an
11113              * @param {Calendar} this
11114              * @param {event}
11115              */
11116         'eventclick': true
11117         
11118     });
11119
11120 };
11121
11122 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
11123     
11124      /**
11125      * @cfg {Number} startDay
11126      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
11127      */
11128     startDay : 0,
11129     
11130     loadMask : false,
11131     
11132     header : false,
11133       
11134     getAutoCreate : function(){
11135         
11136         
11137         var fc_button = function(name, corner, style, content ) {
11138             return Roo.apply({},{
11139                 tag : 'span',
11140                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
11141                          (corner.length ?
11142                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
11143                             ''
11144                         ),
11145                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
11146                 unselectable: 'on'
11147             });
11148         };
11149         
11150         var header = {};
11151         
11152         if(!this.header){
11153             header = {
11154                 tag : 'table',
11155                 cls : 'fc-header',
11156                 style : 'width:100%',
11157                 cn : [
11158                     {
11159                         tag: 'tr',
11160                         cn : [
11161                             {
11162                                 tag : 'td',
11163                                 cls : 'fc-header-left',
11164                                 cn : [
11165                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
11166                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
11167                                     { tag: 'span', cls: 'fc-header-space' },
11168                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
11169
11170
11171                                 ]
11172                             },
11173
11174                             {
11175                                 tag : 'td',
11176                                 cls : 'fc-header-center',
11177                                 cn : [
11178                                     {
11179                                         tag: 'span',
11180                                         cls: 'fc-header-title',
11181                                         cn : {
11182                                             tag: 'H2',
11183                                             html : 'month / year'
11184                                         }
11185                                     }
11186
11187                                 ]
11188                             },
11189                             {
11190                                 tag : 'td',
11191                                 cls : 'fc-header-right',
11192                                 cn : [
11193                               /*      fc_button('month', 'left', '', 'month' ),
11194                                     fc_button('week', '', '', 'week' ),
11195                                     fc_button('day', 'right', '', 'day' )
11196                                 */    
11197
11198                                 ]
11199                             }
11200
11201                         ]
11202                     }
11203                 ]
11204             };
11205         }
11206         
11207         header = this.header;
11208         
11209        
11210         var cal_heads = function() {
11211             var ret = [];
11212             // fixme - handle this.
11213             
11214             for (var i =0; i < Date.dayNames.length; i++) {
11215                 var d = Date.dayNames[i];
11216                 ret.push({
11217                     tag: 'th',
11218                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
11219                     html : d.substring(0,3)
11220                 });
11221                 
11222             }
11223             ret[0].cls += ' fc-first';
11224             ret[6].cls += ' fc-last';
11225             return ret;
11226         };
11227         var cal_cell = function(n) {
11228             return  {
11229                 tag: 'td',
11230                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
11231                 cn : [
11232                     {
11233                         cn : [
11234                             {
11235                                 cls: 'fc-day-number',
11236                                 html: 'D'
11237                             },
11238                             {
11239                                 cls: 'fc-day-content',
11240                              
11241                                 cn : [
11242                                      {
11243                                         style: 'position: relative;' // height: 17px;
11244                                     }
11245                                 ]
11246                             }
11247                             
11248                             
11249                         ]
11250                     }
11251                 ]
11252                 
11253             }
11254         };
11255         var cal_rows = function() {
11256             
11257             var ret = []
11258             for (var r = 0; r < 6; r++) {
11259                 var row= {
11260                     tag : 'tr',
11261                     cls : 'fc-week',
11262                     cn : []
11263                 };
11264                 
11265                 for (var i =0; i < Date.dayNames.length; i++) {
11266                     var d = Date.dayNames[i];
11267                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
11268
11269                 }
11270                 row.cn[0].cls+=' fc-first';
11271                 row.cn[0].cn[0].style = 'min-height:90px';
11272                 row.cn[6].cls+=' fc-last';
11273                 ret.push(row);
11274                 
11275             }
11276             ret[0].cls += ' fc-first';
11277             ret[4].cls += ' fc-prev-last';
11278             ret[5].cls += ' fc-last';
11279             return ret;
11280             
11281         };
11282         
11283         var cal_table = {
11284             tag: 'table',
11285             cls: 'fc-border-separate',
11286             style : 'width:100%',
11287             cellspacing  : 0,
11288             cn : [
11289                 { 
11290                     tag: 'thead',
11291                     cn : [
11292                         { 
11293                             tag: 'tr',
11294                             cls : 'fc-first fc-last',
11295                             cn : cal_heads()
11296                         }
11297                     ]
11298                 },
11299                 { 
11300                     tag: 'tbody',
11301                     cn : cal_rows()
11302                 }
11303                   
11304             ]
11305         };
11306          
11307          var cfg = {
11308             cls : 'fc fc-ltr',
11309             cn : [
11310                 header,
11311                 {
11312                     cls : 'fc-content',
11313                     style : "position: relative;",
11314                     cn : [
11315                         {
11316                             cls : 'fc-view fc-view-month fc-grid',
11317                             style : 'position: relative',
11318                             unselectable : 'on',
11319                             cn : [
11320                                 {
11321                                     cls : 'fc-event-container',
11322                                     style : 'position:absolute;z-index:8;top:0;left:0;'
11323                                 },
11324                                 cal_table
11325                             ]
11326                         }
11327                     ]
11328     
11329                 }
11330            ] 
11331             
11332         };
11333         
11334          
11335         
11336         return cfg;
11337     },
11338     
11339     
11340     initEvents : function()
11341     {
11342         if(!this.store){
11343             throw "can not find store for calendar";
11344         }
11345         
11346         var mark = {
11347             tag: "div",
11348             cls:"x-dlg-mask",
11349             style: "text-align:center",
11350             cn: [
11351                 {
11352                     tag: "div",
11353                     style: "background-color:white;width:50%;margin:250 auto",
11354                     cn: [
11355                         {
11356                             tag: "img",
11357                             src: rootURL + '/roojs1/images/ux/lightbox/loading.gif'
11358                         },
11359                         {
11360                             tag: "span",
11361                             html: "Loading"
11362                         }
11363                         
11364                     ]
11365                 }
11366             ]
11367         }
11368         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
11369         
11370         var size = this.el.select('.fc-content', true).first().getSize();
11371         this.maskEl.setSize(size.width, size.height);
11372         this.maskEl.enableDisplayMode("block");
11373         if(!this.loadMask){
11374             this.maskEl.hide();
11375         }
11376         
11377         this.store = Roo.factory(this.store, Roo.data);
11378         this.store.on('load', this.onLoad, this);
11379         this.store.on('beforeload', this.onBeforeLoad, this);
11380         
11381         this.resize();
11382         
11383         this.cells = this.el.select('.fc-day',true);
11384         //Roo.log(this.cells);
11385         this.textNodes = this.el.query('.fc-day-number');
11386         this.cells.addClassOnOver('fc-state-hover');
11387         
11388         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
11389         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
11390         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
11391         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
11392         
11393         this.on('monthchange', this.onMonthChange, this);
11394         
11395         this.update(new Date().clearTime());
11396     },
11397     
11398     resize : function() {
11399         var sz  = this.el.getSize();
11400         
11401         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
11402         this.el.select('.fc-day-content div',true).setHeight(34);
11403     },
11404     
11405     
11406     // private
11407     showPrevMonth : function(e){
11408         this.update(this.activeDate.add("mo", -1));
11409     },
11410     showToday : function(e){
11411         this.update(new Date().clearTime());
11412     },
11413     // private
11414     showNextMonth : function(e){
11415         this.update(this.activeDate.add("mo", 1));
11416     },
11417
11418     // private
11419     showPrevYear : function(){
11420         this.update(this.activeDate.add("y", -1));
11421     },
11422
11423     // private
11424     showNextYear : function(){
11425         this.update(this.activeDate.add("y", 1));
11426     },
11427
11428     
11429    // private
11430     update : function(date)
11431     {
11432         var vd = this.activeDate;
11433         this.activeDate = date;
11434 //        if(vd && this.el){
11435 //            var t = date.getTime();
11436 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
11437 //                Roo.log('using add remove');
11438 //                
11439 //                this.fireEvent('monthchange', this, date);
11440 //                
11441 //                this.cells.removeClass("fc-state-highlight");
11442 //                this.cells.each(function(c){
11443 //                   if(c.dateValue == t){
11444 //                       c.addClass("fc-state-highlight");
11445 //                       setTimeout(function(){
11446 //                            try{c.dom.firstChild.focus();}catch(e){}
11447 //                       }, 50);
11448 //                       return false;
11449 //                   }
11450 //                   return true;
11451 //                });
11452 //                return;
11453 //            }
11454 //        }
11455         
11456         var days = date.getDaysInMonth();
11457         
11458         var firstOfMonth = date.getFirstDateOfMonth();
11459         var startingPos = firstOfMonth.getDay()-this.startDay;
11460         
11461         if(startingPos < this.startDay){
11462             startingPos += 7;
11463         }
11464         
11465         var pm = date.add(Date.MONTH, -1);
11466         var prevStart = pm.getDaysInMonth()-startingPos;
11467 //        
11468         this.cells = this.el.select('.fc-day',true);
11469         this.textNodes = this.el.query('.fc-day-number');
11470         this.cells.addClassOnOver('fc-state-hover');
11471         
11472         var cells = this.cells.elements;
11473         var textEls = this.textNodes;
11474         
11475         Roo.each(cells, function(cell){
11476             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
11477         });
11478         
11479         days += startingPos;
11480
11481         // convert everything to numbers so it's fast
11482         var day = 86400000;
11483         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
11484         //Roo.log(d);
11485         //Roo.log(pm);
11486         //Roo.log(prevStart);
11487         
11488         var today = new Date().clearTime().getTime();
11489         var sel = date.clearTime().getTime();
11490         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
11491         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
11492         var ddMatch = this.disabledDatesRE;
11493         var ddText = this.disabledDatesText;
11494         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
11495         var ddaysText = this.disabledDaysText;
11496         var format = this.format;
11497         
11498         var setCellClass = function(cal, cell){
11499             
11500             //Roo.log('set Cell Class');
11501             cell.title = "";
11502             var t = d.getTime();
11503             
11504             //Roo.log(d);
11505             
11506             cell.dateValue = t;
11507             if(t == today){
11508                 cell.className += " fc-today";
11509                 cell.className += " fc-state-highlight";
11510                 cell.title = cal.todayText;
11511             }
11512             if(t == sel){
11513                 // disable highlight in other month..
11514                 //cell.className += " fc-state-highlight";
11515                 
11516             }
11517             // disabling
11518             if(t < min) {
11519                 cell.className = " fc-state-disabled";
11520                 cell.title = cal.minText;
11521                 return;
11522             }
11523             if(t > max) {
11524                 cell.className = " fc-state-disabled";
11525                 cell.title = cal.maxText;
11526                 return;
11527             }
11528             if(ddays){
11529                 if(ddays.indexOf(d.getDay()) != -1){
11530                     cell.title = ddaysText;
11531                     cell.className = " fc-state-disabled";
11532                 }
11533             }
11534             if(ddMatch && format){
11535                 var fvalue = d.dateFormat(format);
11536                 if(ddMatch.test(fvalue)){
11537                     cell.title = ddText.replace("%0", fvalue);
11538                     cell.className = " fc-state-disabled";
11539                 }
11540             }
11541             
11542             if (!cell.initialClassName) {
11543                 cell.initialClassName = cell.dom.className;
11544             }
11545             
11546             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
11547         };
11548
11549         var i = 0;
11550         
11551         for(; i < startingPos; i++) {
11552             textEls[i].innerHTML = (++prevStart);
11553             d.setDate(d.getDate()+1);
11554             
11555             cells[i].className = "fc-past fc-other-month";
11556             setCellClass(this, cells[i]);
11557         }
11558         
11559         var intDay = 0;
11560         
11561         for(; i < days; i++){
11562             intDay = i - startingPos + 1;
11563             textEls[i].innerHTML = (intDay);
11564             d.setDate(d.getDate()+1);
11565             
11566             cells[i].className = ''; // "x-date-active";
11567             setCellClass(this, cells[i]);
11568         }
11569         var extraDays = 0;
11570         
11571         for(; i < 42; i++) {
11572             textEls[i].innerHTML = (++extraDays);
11573             d.setDate(d.getDate()+1);
11574             
11575             cells[i].className = "fc-future fc-other-month";
11576             setCellClass(this, cells[i]);
11577         }
11578         
11579         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
11580         
11581         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
11582         
11583         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
11584         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
11585         
11586         if(totalRows != 6){
11587             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
11588             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
11589         }
11590         
11591         this.fireEvent('monthchange', this, date);
11592         
11593         
11594         /*
11595         if(!this.internalRender){
11596             var main = this.el.dom.firstChild;
11597             var w = main.offsetWidth;
11598             this.el.setWidth(w + this.el.getBorderWidth("lr"));
11599             Roo.fly(main).setWidth(w);
11600             this.internalRender = true;
11601             // opera does not respect the auto grow header center column
11602             // then, after it gets a width opera refuses to recalculate
11603             // without a second pass
11604             if(Roo.isOpera && !this.secondPass){
11605                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
11606                 this.secondPass = true;
11607                 this.update.defer(10, this, [date]);
11608             }
11609         }
11610         */
11611         
11612     },
11613     
11614     findCell : function(dt) {
11615         dt = dt.clearTime().getTime();
11616         var ret = false;
11617         this.cells.each(function(c){
11618             //Roo.log("check " +c.dateValue + '?=' + dt);
11619             if(c.dateValue == dt){
11620                 ret = c;
11621                 return false;
11622             }
11623             return true;
11624         });
11625         
11626         return ret;
11627     },
11628     
11629     findCells : function(ev) {
11630         var s = ev.start.clone().clearTime().getTime();
11631        // Roo.log(s);
11632         var e= ev.end.clone().clearTime().getTime();
11633        // Roo.log(e);
11634         var ret = [];
11635         this.cells.each(function(c){
11636              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
11637             
11638             if(c.dateValue > e){
11639                 return ;
11640             }
11641             if(c.dateValue < s){
11642                 return ;
11643             }
11644             ret.push(c);
11645         });
11646         
11647         return ret;    
11648     },
11649     
11650 //    findBestRow: function(cells)
11651 //    {
11652 //        var ret = 0;
11653 //        
11654 //        for (var i =0 ; i < cells.length;i++) {
11655 //            ret  = Math.max(cells[i].rows || 0,ret);
11656 //        }
11657 //        return ret;
11658 //        
11659 //    },
11660     
11661     
11662     addItem : function(ev)
11663     {
11664         // look for vertical location slot in
11665         var cells = this.findCells(ev);
11666         
11667 //        ev.row = this.findBestRow(cells);
11668         
11669         // work out the location.
11670         
11671         var crow = false;
11672         var rows = [];
11673         for(var i =0; i < cells.length; i++) {
11674             if (!crow) {
11675                 crow = {
11676                     start : cells[i],
11677                     end :  cells[i]
11678                 };
11679                 continue;
11680             }
11681             if (crow.start.getY() == cells[i].getY()) {
11682                 // on same row.
11683                 crow.end = cells[i];
11684                 continue;
11685             }
11686             // different row.
11687             rows.push(crow);
11688             crow = {
11689                 start: cells[i],
11690                 end : cells[i]
11691             };
11692             
11693         }
11694         
11695         rows.push(crow);
11696         ev.els = [];
11697         ev.rows = rows;
11698         ev.cells = cells;
11699         ev.rendered = false;
11700 //        for (var i = 0; i < cells.length;i++) {
11701 //            cells[i].rows = Math.max(cells[i].rows || 0 , ev.row + 1 );
11702 //            
11703 //        }
11704         
11705         this.calevents.push(ev);
11706     },
11707     
11708     clearEvents: function() {
11709         
11710         if(!this.calevents){
11711             return;
11712         }
11713         
11714         Roo.each(this.cells.elements, function(c){
11715             c.rows = [];
11716             c.more = [];
11717         });
11718         
11719         Roo.each(this.calevents, function(e) {
11720             Roo.each(e.els, function(el) {
11721                 el.un('mouseenter' ,this.onEventEnter, this);
11722                 el.un('mouseleave' ,this.onEventLeave, this);
11723                 el.remove();
11724             },this);
11725         },this);
11726         
11727         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
11728             e.remove();
11729         });
11730         
11731     },
11732     
11733     renderEvents: function()
11734     {   
11735         // first make sure there is enough space..
11736         this.cells.each(function(c) {
11737             c.rows = [];
11738             c.more = [];
11739         });
11740         
11741         for (var e = 0; e < this.calevents.length; e++) {
11742             
11743             var ev = this.calevents[e];
11744             var cells = ev.cells;
11745             var rows = ev.rows;
11746             
11747             for(var i = 0; i < cells.length; i++){
11748                 
11749                 var cbox = this.cells.item(this.cells.indexOf(cells[i]));
11750                 
11751                 if(cells.length < 2 && cbox.rows.length > 3){
11752                     cbox.more.push(ev);
11753                     continue;
11754                 }
11755                 
11756                 cbox.rows.push(ev);
11757             }
11758         }
11759         
11760         var _this = this;
11761         
11762         this.cells.each(function(c) {
11763             if(c.more.length && c.more.length == 1){
11764                 c.rows.push(c.more.pop());
11765             }
11766             
11767             var r = (c.more.length) ? c.rows.length + 1 : c.rows.length;
11768             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, r * 20));
11769             
11770             
11771             for (var e = 0; e < c.rows.length; e++){
11772                 var ev = c.rows[e];
11773                 
11774                 if(ev.rendered){
11775                     continue;
11776                 }
11777                 
11778                 var cells = ev.cells;
11779                 var rows = ev.rows;
11780                 
11781                 for(var i = 0; i < rows.length; i++) {
11782                 
11783                     // how many rows should it span..
11784
11785                     var  cfg = {
11786                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
11787                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
11788
11789                         unselectable : "on",
11790                         cn : [
11791                             {
11792                                 cls: 'fc-event-inner',
11793                                 cn : [
11794     //                                {
11795     //                                  tag:'span',
11796     //                                  cls: 'fc-event-time',
11797     //                                  html : cells.length > 1 ? '' : ev.time
11798     //                                },
11799                                     {
11800                                       tag:'span',
11801                                       cls: 'fc-event-title',
11802                                       html : String.format('{0}', ev.title)
11803                                     }
11804
11805
11806                                 ]
11807                             },
11808                             {
11809                                 cls: 'ui-resizable-handle ui-resizable-e',
11810                                 html : '&nbsp;&nbsp;&nbsp'
11811                             }
11812
11813                         ]
11814                     };
11815
11816                     if (i == 0) {
11817                         cfg.cls += ' fc-event-start';
11818                     }
11819                     if ((i+1) == rows.length) {
11820                         cfg.cls += ' fc-event-end';
11821                     }
11822
11823                     var ctr = _this.el.select('.fc-event-container',true).first();
11824                     var cg = ctr.createChild(cfg);
11825
11826                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
11827                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
11828
11829                     cg.setXY([sbox.x +2, sbox.y +(e * 20)]);    
11830                     cg.setWidth(ebox.right - sbox.x -2);
11831
11832                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
11833                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
11834                     cg.on('click', _this.onEventClick, _this, ev);
11835
11836                     ev.els.push(cg);
11837                     
11838                     ev.rendered = true;
11839                 }
11840                 
11841             }
11842             
11843             
11844             if(c.more.length){
11845                 var  cfg = {
11846                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
11847                     style : 'position: absolute',
11848                     unselectable : "on",
11849                     cn : [
11850                         {
11851                             cls: 'fc-event-inner',
11852                             cn : [
11853                                 {
11854                                   tag:'span',
11855                                   cls: 'fc-event-title',
11856                                   html : 'More'
11857                                 }
11858
11859
11860                             ]
11861                         },
11862                         {
11863                             cls: 'ui-resizable-handle ui-resizable-e',
11864                             html : '&nbsp;&nbsp;&nbsp'
11865                         }
11866
11867                     ]
11868                 };
11869
11870                 var ctr = _this.el.select('.fc-event-container',true).first();
11871                 var cg = ctr.createChild(cfg);
11872
11873                 var sbox = c.select('.fc-day-content',true).first().getBox();
11874                 var ebox = c.select('.fc-day-content',true).first().getBox();
11875                 //Roo.log(cg);
11876                 cg.setXY([sbox.x +2, sbox.y +(c.rows.length * 20)]);    
11877                 cg.setWidth(ebox.right - sbox.x -2);
11878
11879                 cg.on('click', _this.onMoreEventClick, _this, c.more);
11880                 
11881             }
11882             
11883         });
11884         
11885         
11886         
11887     },
11888     
11889     onEventEnter: function (e, el,event,d) {
11890         this.fireEvent('evententer', this, el, event);
11891     },
11892     
11893     onEventLeave: function (e, el,event,d) {
11894         this.fireEvent('eventleave', this, el, event);
11895     },
11896     
11897     onEventClick: function (e, el,event,d) {
11898         this.fireEvent('eventclick', this, el, event);
11899     },
11900     
11901     onMonthChange: function () {
11902         this.store.load();
11903     },
11904     
11905     onMoreEventClick: function(e, el, more)
11906     {
11907         var _this = this;
11908         
11909         this.calpopover.placement = 'right';
11910         this.calpopover.setTitle('More');
11911         
11912         this.calpopover.setContent('');
11913         
11914         var ctr = this.calpopover.el.select('.popover-content', true).first();
11915         
11916         Roo.each(more, function(m){
11917             var cfg = {
11918                 cls : 'fc-event-hori fc-event-draggable',
11919                 html : m.title
11920             }
11921             var cg = ctr.createChild(cfg);
11922             
11923             cg.on('click', _this.onEventClick, _this, m);
11924         });
11925         
11926         this.calpopover.show(el);
11927         
11928         
11929     },
11930     
11931     onLoad: function () 
11932     {   
11933         this.calevents = [];
11934         var cal = this;
11935         
11936         if(this.store.getCount() > 0){
11937             this.store.data.each(function(d){
11938                cal.addItem({
11939                     id : d.data.id,
11940                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
11941                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
11942                     time : d.data.start_time,
11943                     title : d.data.title,
11944                     description : d.data.description,
11945                     venue : d.data.venue
11946                 });
11947             });
11948         }
11949         
11950         this.renderEvents();
11951         
11952         if(this.calevents.length && this.loadMask){
11953             this.maskEl.hide();
11954         }
11955     },
11956     
11957     onBeforeLoad: function()
11958     {
11959         this.clearEvents();
11960         if(this.loadMask){
11961             this.maskEl.show();
11962         }
11963     }
11964 });
11965
11966  
11967  /*
11968  * - LGPL
11969  *
11970  * element
11971  * 
11972  */
11973
11974 /**
11975  * @class Roo.bootstrap.Popover
11976  * @extends Roo.bootstrap.Component
11977  * Bootstrap Popover class
11978  * @cfg {String} html contents of the popover   (or false to use children..)
11979  * @cfg {String} title of popover (or false to hide)
11980  * @cfg {String} placement how it is placed
11981  * @cfg {String} trigger click || hover (or false to trigger manually)
11982  * @cfg {String} over what (parent or false to trigger manually.)
11983  * 
11984  * @constructor
11985  * Create a new Popover
11986  * @param {Object} config The config object
11987  */
11988
11989 Roo.bootstrap.Popover = function(config){
11990     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
11991 };
11992
11993 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
11994     
11995     title: 'Fill in a title',
11996     html: false,
11997     
11998     placement : 'right',
11999     trigger : 'hover', // hover
12000     
12001     over: 'parent',
12002     
12003     can_build_overlaid : false,
12004     
12005     getChildContainer : function()
12006     {
12007         return this.el.select('.popover-content',true).first();
12008     },
12009     
12010     getAutoCreate : function(){
12011          Roo.log('make popover?');
12012         var cfg = {
12013            cls : 'popover roo-dynamic',
12014            style: 'display:block',
12015            cn : [
12016                 {
12017                     cls : 'arrow'
12018                 },
12019                 {
12020                     cls : 'popover-inner',
12021                     cn : [
12022                         {
12023                             tag: 'h3',
12024                             cls: 'popover-title',
12025                             html : this.title
12026                         },
12027                         {
12028                             cls : 'popover-content',
12029                             html : this.html
12030                         }
12031                     ]
12032                     
12033                 }
12034            ]
12035         };
12036         
12037         return cfg;
12038     },
12039     setTitle: function(str)
12040     {
12041         this.el.select('.popover-title',true).first().dom.innerHTML = str;
12042     },
12043     setContent: function(str)
12044     {
12045         this.el.select('.popover-content',true).first().dom.innerHTML = str;
12046     },
12047     // as it get's added to the bottom of the page.
12048     onRender : function(ct, position)
12049     {
12050         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
12051         if(!this.el){
12052             var cfg = Roo.apply({},  this.getAutoCreate());
12053             cfg.id = Roo.id();
12054             
12055             if (this.cls) {
12056                 cfg.cls += ' ' + this.cls;
12057             }
12058             if (this.style) {
12059                 cfg.style = this.style;
12060             }
12061             Roo.log("adding to ")
12062             this.el = Roo.get(document.body).createChild(cfg, position);
12063             Roo.log(this.el);
12064         }
12065         this.initEvents();
12066     },
12067     
12068     initEvents : function()
12069     {
12070         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
12071         this.el.enableDisplayMode('block');
12072         this.el.hide();
12073         if (this.over === false) {
12074             return; 
12075         }
12076         if (this.triggers === false) {
12077             return;
12078         }
12079         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
12080         var triggers = this.trigger ? this.trigger.split(' ') : [];
12081         Roo.each(triggers, function(trigger) {
12082         
12083             if (trigger == 'click') {
12084                 on_el.on('click', this.toggle, this);
12085             } else if (trigger != 'manual') {
12086                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
12087                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
12088       
12089                 on_el.on(eventIn  ,this.enter, this);
12090                 on_el.on(eventOut, this.leave, this);
12091             }
12092         }, this);
12093         
12094     },
12095     
12096     
12097     // private
12098     timeout : null,
12099     hoverState : null,
12100     
12101     toggle : function () {
12102         this.hoverState == 'in' ? this.leave() : this.enter();
12103     },
12104     
12105     enter : function () {
12106        
12107     
12108         clearTimeout(this.timeout);
12109     
12110         this.hoverState = 'in'
12111     
12112         if (!this.delay || !this.delay.show) {
12113             this.show();
12114             return 
12115         }
12116         var _t = this;
12117         this.timeout = setTimeout(function () {
12118             if (_t.hoverState == 'in') {
12119                 _t.show();
12120             }
12121         }, this.delay.show)
12122     },
12123     leave : function() {
12124         clearTimeout(this.timeout);
12125     
12126         this.hoverState = 'out'
12127     
12128         if (!this.delay || !this.delay.hide) {
12129             this.hide();
12130             return 
12131         }
12132         var _t = this;
12133         this.timeout = setTimeout(function () {
12134             if (_t.hoverState == 'out') {
12135                 _t.hide();
12136             }
12137         }, this.delay.hide)
12138     },
12139     
12140     show : function (on_el)
12141     {
12142         if (!on_el) {
12143             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
12144         }
12145         // set content.
12146         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
12147         if (this.html !== false) {
12148             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
12149         }
12150         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
12151         if (!this.title.length) {
12152             this.el.select('.popover-title',true).hide();
12153         }
12154         
12155         var placement = typeof this.placement == 'function' ?
12156             this.placement.call(this, this.el, on_el) :
12157             this.placement;
12158             
12159         var autoToken = /\s?auto?\s?/i;
12160         var autoPlace = autoToken.test(placement);
12161         if (autoPlace) {
12162             placement = placement.replace(autoToken, '') || 'top';
12163         }
12164         
12165         //this.el.detach()
12166         //this.el.setXY([0,0]);
12167         this.el.show();
12168         this.el.dom.style.display='block';
12169         this.el.addClass(placement);
12170         
12171         //this.el.appendTo(on_el);
12172         
12173         var p = this.getPosition();
12174         var box = this.el.getBox();
12175         
12176         if (autoPlace) {
12177             // fixme..
12178         }
12179         var align = Roo.bootstrap.Popover.alignment[placement]
12180         this.el.alignTo(on_el, align[0],align[1]);
12181         //var arrow = this.el.select('.arrow',true).first();
12182         //arrow.set(align[2], 
12183         
12184         this.el.addClass('in');
12185         this.hoverState = null;
12186         
12187         if (this.el.hasClass('fade')) {
12188             // fade it?
12189         }
12190         
12191     },
12192     hide : function()
12193     {
12194         this.el.setXY([0,0]);
12195         this.el.removeClass('in');
12196         this.el.hide();
12197         
12198     }
12199     
12200 });
12201
12202 Roo.bootstrap.Popover.alignment = {
12203     'left' : ['r-l', [-10,0], 'right'],
12204     'right' : ['l-r', [10,0], 'left'],
12205     'bottom' : ['t-b', [0,10], 'top'],
12206     'top' : [ 'b-t', [0,-10], 'bottom']
12207 };
12208
12209  /*
12210  * - LGPL
12211  *
12212  * Progress
12213  * 
12214  */
12215
12216 /**
12217  * @class Roo.bootstrap.Progress
12218  * @extends Roo.bootstrap.Component
12219  * Bootstrap Progress class
12220  * @cfg {Boolean} striped striped of the progress bar
12221  * @cfg {Boolean} active animated of the progress bar
12222  * 
12223  * 
12224  * @constructor
12225  * Create a new Progress
12226  * @param {Object} config The config object
12227  */
12228
12229 Roo.bootstrap.Progress = function(config){
12230     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
12231 };
12232
12233 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
12234     
12235     striped : false,
12236     active: false,
12237     
12238     getAutoCreate : function(){
12239         var cfg = {
12240             tag: 'div',
12241             cls: 'progress'
12242         };
12243         
12244         
12245         if(this.striped){
12246             cfg.cls += ' progress-striped';
12247         }
12248       
12249         if(this.active){
12250             cfg.cls += ' active';
12251         }
12252         
12253         
12254         return cfg;
12255     }
12256    
12257 });
12258
12259  
12260
12261  /*
12262  * - LGPL
12263  *
12264  * ProgressBar
12265  * 
12266  */
12267
12268 /**
12269  * @class Roo.bootstrap.ProgressBar
12270  * @extends Roo.bootstrap.Component
12271  * Bootstrap ProgressBar class
12272  * @cfg {Number} aria_valuenow aria-value now
12273  * @cfg {Number} aria_valuemin aria-value min
12274  * @cfg {Number} aria_valuemax aria-value max
12275  * @cfg {String} label label for the progress bar
12276  * @cfg {String} panel (success | info | warning | danger )
12277  * @cfg {String} role role of the progress bar
12278  * @cfg {String} sr_only text
12279  * 
12280  * 
12281  * @constructor
12282  * Create a new ProgressBar
12283  * @param {Object} config The config object
12284  */
12285
12286 Roo.bootstrap.ProgressBar = function(config){
12287     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
12288 };
12289
12290 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
12291     
12292     aria_valuenow : 0,
12293     aria_valuemin : 0,
12294     aria_valuemax : 100,
12295     label : false,
12296     panel : false,
12297     role : false,
12298     sr_only: false,
12299     
12300     getAutoCreate : function()
12301     {
12302         
12303         var cfg = {
12304             tag: 'div',
12305             cls: 'progress-bar',
12306             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
12307         };
12308         
12309         if(this.sr_only){
12310             cfg.cn = {
12311                 tag: 'span',
12312                 cls: 'sr-only',
12313                 html: this.sr_only
12314             }
12315         }
12316         
12317         if(this.role){
12318             cfg.role = this.role;
12319         }
12320         
12321         if(this.aria_valuenow){
12322             cfg['aria-valuenow'] = this.aria_valuenow;
12323         }
12324         
12325         if(this.aria_valuemin){
12326             cfg['aria-valuemin'] = this.aria_valuemin;
12327         }
12328         
12329         if(this.aria_valuemax){
12330             cfg['aria-valuemax'] = this.aria_valuemax;
12331         }
12332         
12333         if(this.label && !this.sr_only){
12334             cfg.html = this.label;
12335         }
12336         
12337         if(this.panel){
12338             cfg.cls += ' progress-bar-' + this.panel;
12339         }
12340         
12341         return cfg;
12342     },
12343     
12344     update : function(aria_valuenow)
12345     {
12346         this.aria_valuenow = aria_valuenow;
12347         
12348         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
12349     }
12350    
12351 });
12352
12353  
12354
12355  /*
12356  * - LGPL
12357  *
12358  * TabPanel
12359  * 
12360  */
12361
12362 /**
12363  * @class Roo.bootstrap.TabPanel
12364  * @extends Roo.bootstrap.Component
12365  * Bootstrap TabPanel class
12366  * @cfg {Boolean} active panel active
12367  * @cfg {String} html panel content
12368  * @cfg {String} tabId tab relate id
12369  * @cfg {String} navId The navbar which triggers show hide
12370  * 
12371  * 
12372  * @constructor
12373  * Create a new TabPanel
12374  * @param {Object} config The config object
12375  */
12376
12377 Roo.bootstrap.TabPanel = function(config){
12378     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
12379      this.addEvents({
12380         /**
12381              * @event changed
12382              * Fires when the active status changes
12383              * @param {Roo.bootstrap.TabPanel} this
12384              * @param {Boolean} state the new state
12385             
12386          */
12387         'changed': true
12388      });
12389 };
12390
12391 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
12392     
12393     active: false,
12394     html: false,
12395     tabId: false,
12396     navId : false,
12397     
12398     getAutoCreate : function(){
12399         var cfg = {
12400             tag: 'div',
12401             cls: 'tab-pane',
12402             html: this.html || ''
12403         };
12404         
12405         if(this.active){
12406             cfg.cls += ' active';
12407         }
12408         
12409         if(this.tabId){
12410             cfg.tabId = this.tabId;
12411         }
12412         
12413         return cfg;
12414     },
12415     onRender : function(ct, position)
12416     {
12417        // Roo.log("Call onRender: " + this.xtype);
12418         
12419         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
12420         
12421         if (this.navId && this.tabId) {
12422             var item = Roo.bootstrap.NavGroup.get(this.navId).getNavItem(this.tabId);
12423             if (!item) {
12424                 Roo.log("could not find navID:"  + this.navId + ", tabId: " + this.tabId);
12425             } else {
12426                 item.on('changed', function(item, state) {
12427                     this.setActive(state);
12428                 }, this);
12429             }
12430         }
12431         
12432     },
12433     setActive: function(state)
12434     {
12435         Roo.log("panel - set active " + this.tabId + "=" + state);
12436         
12437         this.active = state;
12438         if (!state) {
12439             this.el.removeClass('active');
12440             
12441         } else  if (!this.el.hasClass('active')) {
12442             this.el.addClass('active');
12443         }
12444         this.fireEvent('changed', this, state);
12445     }
12446     
12447     
12448 });
12449  
12450
12451  
12452
12453  /*
12454  * - LGPL
12455  *
12456  * DateField
12457  * 
12458  */
12459
12460 /**
12461  * @class Roo.bootstrap.DateField
12462  * @extends Roo.bootstrap.Input
12463  * Bootstrap DateField class
12464  * @cfg {Number} weekStart default 0
12465  * @cfg {Number} weekStart default 0
12466  * @cfg {Number} viewMode default empty, (months|years)
12467  * @cfg {Number} minViewMode default empty, (months|years)
12468  * @cfg {Number} startDate default -Infinity
12469  * @cfg {Number} endDate default Infinity
12470  * @cfg {Boolean} todayHighlight default false
12471  * @cfg {Boolean} todayBtn default false
12472  * @cfg {Boolean} calendarWeeks default false
12473  * @cfg {Object} daysOfWeekDisabled default empty
12474  * 
12475  * @cfg {Boolean} keyboardNavigation default true
12476  * @cfg {String} language default en
12477  * 
12478  * @constructor
12479  * Create a new DateField
12480  * @param {Object} config The config object
12481  */
12482
12483 Roo.bootstrap.DateField = function(config){
12484     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
12485      this.addEvents({
12486             /**
12487              * @event show
12488              * Fires when this field show.
12489              * @param {Roo.bootstrap.DateField} this
12490              * @param {Mixed} date The date value
12491              */
12492             show : true,
12493             /**
12494              * @event show
12495              * Fires when this field hide.
12496              * @param {Roo.bootstrap.DateField} this
12497              * @param {Mixed} date The date value
12498              */
12499             hide : true,
12500             /**
12501              * @event select
12502              * Fires when select a date.
12503              * @param {Roo.bootstrap.DateField} this
12504              * @param {Mixed} date The date value
12505              */
12506             select : true
12507         });
12508 };
12509
12510 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
12511     
12512     /**
12513      * @cfg {String} format
12514      * The default date format string which can be overriden for localization support.  The format must be
12515      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
12516      */
12517     format : "m/d/y",
12518     /**
12519      * @cfg {String} altFormats
12520      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
12521      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
12522      */
12523     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
12524     
12525     weekStart : 0,
12526     
12527     viewMode : '',
12528     
12529     minViewMode : '',
12530     
12531     todayHighlight : false,
12532     
12533     todayBtn: false,
12534     
12535     language: 'en',
12536     
12537     keyboardNavigation: true,
12538     
12539     calendarWeeks: false,
12540     
12541     startDate: -Infinity,
12542     
12543     endDate: Infinity,
12544     
12545     daysOfWeekDisabled: [],
12546     
12547     _events: [],
12548     
12549     UTCDate: function()
12550     {
12551         return new Date(Date.UTC.apply(Date, arguments));
12552     },
12553     
12554     UTCToday: function()
12555     {
12556         var today = new Date();
12557         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
12558     },
12559     
12560     getDate: function() {
12561             var d = this.getUTCDate();
12562             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
12563     },
12564     
12565     getUTCDate: function() {
12566             return this.date;
12567     },
12568     
12569     setDate: function(d) {
12570             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
12571     },
12572     
12573     setUTCDate: function(d) {
12574             this.date = d;
12575             this.setValue(this.formatDate(this.date));
12576     },
12577         
12578     onRender: function(ct, position)
12579     {
12580         
12581         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
12582         
12583         this.language = this.language || 'en';
12584         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
12585         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
12586         
12587         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
12588         this.format = this.format || 'm/d/y';
12589         this.isInline = false;
12590         this.isInput = true;
12591         this.component = this.el.select('.add-on', true).first() || false;
12592         this.component = (this.component && this.component.length === 0) ? false : this.component;
12593         this.hasInput = this.component && this.inputEL().length;
12594         
12595         if (typeof(this.minViewMode === 'string')) {
12596             switch (this.minViewMode) {
12597                 case 'months':
12598                     this.minViewMode = 1;
12599                     break;
12600                 case 'years':
12601                     this.minViewMode = 2;
12602                     break;
12603                 default:
12604                     this.minViewMode = 0;
12605                     break;
12606             }
12607         }
12608         
12609         if (typeof(this.viewMode === 'string')) {
12610             switch (this.viewMode) {
12611                 case 'months':
12612                     this.viewMode = 1;
12613                     break;
12614                 case 'years':
12615                     this.viewMode = 2;
12616                     break;
12617                 default:
12618                     this.viewMode = 0;
12619                     break;
12620             }
12621         }
12622                 
12623         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
12624         
12625         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
12626         
12627         this.picker().on('mousedown', this.onMousedown, this);
12628         this.picker().on('click', this.onClick, this);
12629         
12630         this.picker().addClass('datepicker-dropdown');
12631         
12632         this.startViewMode = this.viewMode;
12633         
12634         
12635         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
12636             if(!this.calendarWeeks){
12637                 v.remove();
12638                 return;
12639             };
12640             
12641             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
12642             v.attr('colspan', function(i, val){
12643                 return parseInt(val) + 1;
12644             });
12645         })
12646                         
12647         
12648         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
12649         
12650         this.setStartDate(this.startDate);
12651         this.setEndDate(this.endDate);
12652         
12653         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
12654         
12655         this.fillDow();
12656         this.fillMonths();
12657         this.update();
12658         this.showMode();
12659         
12660         if(this.isInline) {
12661             this.show();
12662         }
12663     },
12664     
12665     picker : function()
12666     {
12667         return this.el.select('.datepicker', true).first();
12668     },
12669     
12670     fillDow: function()
12671     {
12672         var dowCnt = this.weekStart;
12673         
12674         var dow = {
12675             tag: 'tr',
12676             cn: [
12677                 
12678             ]
12679         };
12680         
12681         if(this.calendarWeeks){
12682             dow.cn.push({
12683                 tag: 'th',
12684                 cls: 'cw',
12685                 html: '&nbsp;'
12686             })
12687         }
12688         
12689         while (dowCnt < this.weekStart + 7) {
12690             dow.cn.push({
12691                 tag: 'th',
12692                 cls: 'dow',
12693                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
12694             });
12695         }
12696         
12697         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
12698     },
12699     
12700     fillMonths: function()
12701     {    
12702         var i = 0
12703         var months = this.picker().select('>.datepicker-months td', true).first();
12704         
12705         months.dom.innerHTML = '';
12706         
12707         while (i < 12) {
12708             var month = {
12709                 tag: 'span',
12710                 cls: 'month',
12711                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
12712             }
12713             
12714             months.createChild(month);
12715         }
12716         
12717     },
12718     
12719     update: function(){
12720         
12721         this.date = (typeof(this.date) === 'undefined') ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
12722         
12723         if (this.date < this.startDate) {
12724             this.viewDate = new Date(this.startDate);
12725         } else if (this.date > this.endDate) {
12726             this.viewDate = new Date(this.endDate);
12727         } else {
12728             this.viewDate = new Date(this.date);
12729         }
12730         
12731         this.fill();
12732     },
12733     
12734     fill: function() {
12735         var d = new Date(this.viewDate),
12736                 year = d.getUTCFullYear(),
12737                 month = d.getUTCMonth(),
12738                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
12739                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
12740                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
12741                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
12742                 currentDate = this.date && this.date.valueOf(),
12743                 today = this.UTCToday();
12744         
12745         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
12746         
12747 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
12748         
12749 //        this.picker.select('>tfoot th.today').
12750 //                                              .text(dates[this.language].today)
12751 //                                              .toggle(this.todayBtn !== false);
12752     
12753         this.updateNavArrows();
12754         this.fillMonths();
12755                                                 
12756         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
12757         
12758         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
12759          
12760         prevMonth.setUTCDate(day);
12761         
12762         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
12763         
12764         var nextMonth = new Date(prevMonth);
12765         
12766         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
12767         
12768         nextMonth = nextMonth.valueOf();
12769         
12770         var fillMonths = false;
12771         
12772         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
12773         
12774         while(prevMonth.valueOf() < nextMonth) {
12775             var clsName = '';
12776             
12777             if (prevMonth.getUTCDay() === this.weekStart) {
12778                 if(fillMonths){
12779                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
12780                 }
12781                     
12782                 fillMonths = {
12783                     tag: 'tr',
12784                     cn: []
12785                 };
12786                 
12787                 if(this.calendarWeeks){
12788                     // ISO 8601: First week contains first thursday.
12789                     // ISO also states week starts on Monday, but we can be more abstract here.
12790                     var
12791                     // Start of current week: based on weekstart/current date
12792                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
12793                     // Thursday of this week
12794                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
12795                     // First Thursday of year, year from thursday
12796                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
12797                     // Calendar week: ms between thursdays, div ms per day, div 7 days
12798                     calWeek =  (th - yth) / 864e5 / 7 + 1;
12799                     
12800                     fillMonths.cn.push({
12801                         tag: 'td',
12802                         cls: 'cw',
12803                         html: calWeek
12804                     });
12805                 }
12806             }
12807             
12808             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
12809                 clsName += ' old';
12810             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
12811                 clsName += ' new';
12812             }
12813             if (this.todayHighlight &&
12814                 prevMonth.getUTCFullYear() == today.getFullYear() &&
12815                 prevMonth.getUTCMonth() == today.getMonth() &&
12816                 prevMonth.getUTCDate() == today.getDate()) {
12817                 clsName += ' today';
12818             }
12819             
12820             if (currentDate && prevMonth.valueOf() === currentDate) {
12821                 clsName += ' active';
12822             }
12823             
12824             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
12825                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
12826                     clsName += ' disabled';
12827             }
12828             
12829             fillMonths.cn.push({
12830                 tag: 'td',
12831                 cls: 'day ' + clsName,
12832                 html: prevMonth.getDate()
12833             })
12834             
12835             prevMonth.setDate(prevMonth.getDate()+1);
12836         }
12837           
12838         var currentYear = this.date && this.date.getUTCFullYear();
12839         var currentMonth = this.date && this.date.getUTCMonth();
12840         
12841         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
12842         
12843         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
12844             v.removeClass('active');
12845             
12846             if(currentYear === year && k === currentMonth){
12847                 v.addClass('active');
12848             }
12849             
12850             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
12851                 v.addClass('disabled');
12852             }
12853             
12854         });
12855         
12856         
12857         year = parseInt(year/10, 10) * 10;
12858         
12859         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
12860         
12861         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
12862         
12863         year -= 1;
12864         for (var i = -1; i < 11; i++) {
12865             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
12866                 tag: 'span',
12867                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
12868                 html: year
12869             })
12870             
12871             year += 1;
12872         }
12873     },
12874     
12875     showMode: function(dir) {
12876         if (dir) {
12877             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
12878         }
12879         Roo.each(this.picker().select('>div',true).elements, function(v){
12880             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
12881             v.hide();
12882         });
12883         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
12884     },
12885     
12886     place: function()
12887     {
12888         if(this.isInline) return;
12889         
12890         this.picker().removeClass(['bottom', 'top']);
12891         
12892         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
12893             /*
12894              * place to the top of element!
12895              *
12896              */
12897             
12898             this.picker().addClass('top');
12899             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12900             
12901             return;
12902         }
12903         
12904         this.picker().addClass('bottom');
12905         
12906         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12907     },
12908     
12909     parseDate : function(value){
12910         if(!value || value instanceof Date){
12911             return value;
12912         }
12913         var v = Date.parseDate(value, this.format);
12914         if (!v && this.useIso) {
12915             v = Date.parseDate(value, 'Y-m-d');
12916         }
12917         if(!v && this.altFormats){
12918             if(!this.altFormatsArray){
12919                 this.altFormatsArray = this.altFormats.split("|");
12920             }
12921             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
12922                 v = Date.parseDate(value, this.altFormatsArray[i]);
12923             }
12924         }
12925         return v;
12926     },
12927     
12928     formatDate : function(date, fmt){
12929         return (!date || !(date instanceof Date)) ?
12930         date : date.dateFormat(fmt || this.format);
12931     },
12932     
12933     onFocus : function()
12934     {
12935         Roo.bootstrap.DateField.superclass.onFocus.call(this);
12936         this.show();
12937     },
12938     
12939     onBlur : function()
12940     {
12941         Roo.bootstrap.DateField.superclass.onBlur.call(this);
12942         this.hide();
12943     },
12944     
12945     show : function()
12946     {
12947         this.picker().show();
12948         this.update();
12949         this.place();
12950         
12951         this.fireEvent('show', this, this.date);
12952     },
12953     
12954     hide : function()
12955     {
12956         if(this.isInline) return;
12957         this.picker().hide();
12958         this.viewMode = this.startViewMode;
12959         this.showMode();
12960         
12961         this.fireEvent('hide', this, this.date);
12962         
12963     },
12964     
12965     onMousedown: function(e){
12966         e.stopPropagation();
12967         e.preventDefault();
12968     },
12969     
12970     keyup: function(e){
12971         Roo.bootstrap.DateField.superclass.keyup.call(this);
12972         this.update();
12973         
12974     },
12975
12976     setValue: function(v){
12977         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
12978         
12979         this.fireEvent('select', this, this.date);
12980         
12981     },
12982     
12983     fireKey: function(e){
12984         if (!this.picker().isVisible()){
12985             if (e.keyCode == 27) // allow escape to hide and re-show picker
12986                 this.show();
12987             return;
12988         }
12989         var dateChanged = false,
12990         dir, day, month,
12991         newDate, newViewDate;
12992         switch(e.keyCode){
12993             case 27: // escape
12994                 this.hide();
12995                 e.preventDefault();
12996                 break;
12997             case 37: // left
12998             case 39: // right
12999                 if (!this.keyboardNavigation) break;
13000                 dir = e.keyCode == 37 ? -1 : 1;
13001                 
13002                 if (e.ctrlKey){
13003                     newDate = this.moveYear(this.date, dir);
13004                     newViewDate = this.moveYear(this.viewDate, dir);
13005                 } else if (e.shiftKey){
13006                     newDate = this.moveMonth(this.date, dir);
13007                     newViewDate = this.moveMonth(this.viewDate, dir);
13008                 } else {
13009                     newDate = new Date(this.date);
13010                     newDate.setUTCDate(this.date.getUTCDate() + dir);
13011                     newViewDate = new Date(this.viewDate);
13012                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
13013                 }
13014                 if (this.dateWithinRange(newDate)){
13015                     this.date = newDate;
13016                     this.viewDate = newViewDate;
13017                     this.setValue(this.formatDate(this.date));
13018                     this.update();
13019                     e.preventDefault();
13020                     dateChanged = true;
13021                 }
13022                 break;
13023             case 38: // up
13024             case 40: // down
13025                 if (!this.keyboardNavigation) break;
13026                 dir = e.keyCode == 38 ? -1 : 1;
13027                 if (e.ctrlKey){
13028                     newDate = this.moveYear(this.date, dir);
13029                     newViewDate = this.moveYear(this.viewDate, dir);
13030                 } else if (e.shiftKey){
13031                     newDate = this.moveMonth(this.date, dir);
13032                     newViewDate = this.moveMonth(this.viewDate, dir);
13033                 } else {
13034                     newDate = new Date(this.date);
13035                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
13036                     newViewDate = new Date(this.viewDate);
13037                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
13038                 }
13039                 if (this.dateWithinRange(newDate)){
13040                     this.date = newDate;
13041                     this.viewDate = newViewDate;
13042                     this.setValue(this.formatDate(this.date));
13043                     this.update();
13044                     e.preventDefault();
13045                     dateChanged = true;
13046                 }
13047                 break;
13048             case 13: // enter
13049                 this.setValue(this.formatDate(this.date));
13050                 this.hide();
13051                 e.preventDefault();
13052                 break;
13053             case 9: // tab
13054                 this.setValue(this.formatDate(this.date));
13055                 this.hide();
13056                 break;
13057         }
13058     },
13059     
13060     
13061     onClick: function(e) {
13062         e.stopPropagation();
13063         e.preventDefault();
13064         
13065         var target = e.getTarget();
13066         
13067         if(target.nodeName.toLowerCase() === 'i'){
13068             target = Roo.get(target).dom.parentNode;
13069         }
13070         
13071         var nodeName = target.nodeName;
13072         var className = target.className;
13073         var html = target.innerHTML;
13074         
13075         switch(nodeName.toLowerCase()) {
13076             case 'th':
13077                 switch(className) {
13078                     case 'switch':
13079                         this.showMode(1);
13080                         break;
13081                     case 'prev':
13082                     case 'next':
13083                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
13084                         switch(this.viewMode){
13085                                 case 0:
13086                                         this.viewDate = this.moveMonth(this.viewDate, dir);
13087                                         break;
13088                                 case 1:
13089                                 case 2:
13090                                         this.viewDate = this.moveYear(this.viewDate, dir);
13091                                         break;
13092                         }
13093                         this.fill();
13094                         break;
13095                     case 'today':
13096                         var date = new Date();
13097                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
13098                         this.fill()
13099                         this.setValue(this.formatDate(this.date));
13100                         this.hide();
13101                         break;
13102                 }
13103                 break;
13104             case 'span':
13105                 if (className.indexOf('disabled') === -1) {
13106                     this.viewDate.setUTCDate(1);
13107                     if (className.indexOf('month') !== -1) {
13108                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
13109                     } else {
13110                         var year = parseInt(html, 10) || 0;
13111                         this.viewDate.setUTCFullYear(year);
13112                         
13113                     }
13114                     this.showMode(-1);
13115                     this.fill();
13116                 }
13117                 break;
13118                 
13119             case 'td':
13120                 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
13121                     var day = parseInt(html, 10) || 1;
13122                     var year = this.viewDate.getUTCFullYear(),
13123                         month = this.viewDate.getUTCMonth();
13124
13125                     if (className.indexOf('old') !== -1) {
13126                         if(month === 0 ){
13127                             month = 11;
13128                             year -= 1;
13129                         }else{
13130                             month -= 1;
13131                         }
13132                     } else if (className.indexOf('new') !== -1) {
13133                         if (month == 11) {
13134                             month = 0;
13135                             year += 1;
13136                         } else {
13137                             month += 1;
13138                         }
13139                     }
13140                     this.date = this.UTCDate(year, month, day,0,0,0,0);
13141                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
13142                     this.fill();
13143                     this.setValue(this.formatDate(this.date));
13144                     this.hide();
13145                 }
13146                 break;
13147         }
13148     },
13149     
13150     setStartDate: function(startDate){
13151         this.startDate = startDate || -Infinity;
13152         if (this.startDate !== -Infinity) {
13153             this.startDate = this.parseDate(this.startDate);
13154         }
13155         this.update();
13156         this.updateNavArrows();
13157     },
13158
13159     setEndDate: function(endDate){
13160         this.endDate = endDate || Infinity;
13161         if (this.endDate !== Infinity) {
13162             this.endDate = this.parseDate(this.endDate);
13163         }
13164         this.update();
13165         this.updateNavArrows();
13166     },
13167     
13168     setDaysOfWeekDisabled: function(daysOfWeekDisabled){
13169         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
13170         if (typeof(this.daysOfWeekDisabled) !== 'object') {
13171             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
13172         }
13173         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
13174             return parseInt(d, 10);
13175         });
13176         this.update();
13177         this.updateNavArrows();
13178     },
13179     
13180     updateNavArrows: function() {
13181         var d = new Date(this.viewDate),
13182         year = d.getUTCFullYear(),
13183         month = d.getUTCMonth();
13184         
13185         Roo.each(this.picker().select('.prev', true).elements, function(v){
13186             v.show();
13187             switch (this.viewMode) {
13188                 case 0:
13189
13190                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
13191                         v.hide();
13192                     }
13193                     break;
13194                 case 1:
13195                 case 2:
13196                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
13197                         v.hide();
13198                     }
13199                     break;
13200             }
13201         });
13202         
13203         Roo.each(this.picker().select('.next', true).elements, function(v){
13204             v.show();
13205             switch (this.viewMode) {
13206                 case 0:
13207
13208                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
13209                         v.hide();
13210                     }
13211                     break;
13212                 case 1:
13213                 case 2:
13214                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
13215                         v.hide();
13216                     }
13217                     break;
13218             }
13219         })
13220     },
13221     
13222     moveMonth: function(date, dir){
13223         if (!dir) return date;
13224         var new_date = new Date(date.valueOf()),
13225         day = new_date.getUTCDate(),
13226         month = new_date.getUTCMonth(),
13227         mag = Math.abs(dir),
13228         new_month, test;
13229         dir = dir > 0 ? 1 : -1;
13230         if (mag == 1){
13231             test = dir == -1
13232             // If going back one month, make sure month is not current month
13233             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
13234             ? function(){
13235                 return new_date.getUTCMonth() == month;
13236             }
13237             // If going forward one month, make sure month is as expected
13238             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
13239             : function(){
13240                 return new_date.getUTCMonth() != new_month;
13241             };
13242             new_month = month + dir;
13243             new_date.setUTCMonth(new_month);
13244             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
13245             if (new_month < 0 || new_month > 11)
13246                 new_month = (new_month + 12) % 12;
13247         } else {
13248             // For magnitudes >1, move one month at a time...
13249             for (var i=0; i<mag; i++)
13250                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
13251                 new_date = this.moveMonth(new_date, dir);
13252             // ...then reset the day, keeping it in the new month
13253             new_month = new_date.getUTCMonth();
13254             new_date.setUTCDate(day);
13255             test = function(){
13256                 return new_month != new_date.getUTCMonth();
13257             };
13258         }
13259         // Common date-resetting loop -- if date is beyond end of month, make it
13260         // end of month
13261         while (test()){
13262             new_date.setUTCDate(--day);
13263             new_date.setUTCMonth(new_month);
13264         }
13265         return new_date;
13266     },
13267
13268     moveYear: function(date, dir){
13269         return this.moveMonth(date, dir*12);
13270     },
13271
13272     dateWithinRange: function(date){
13273         return date >= this.startDate && date <= this.endDate;
13274     },
13275
13276     
13277     remove: function() {
13278         this.picker().remove();
13279     }
13280    
13281 });
13282
13283 Roo.apply(Roo.bootstrap.DateField,  {
13284     
13285     head : {
13286         tag: 'thead',
13287         cn: [
13288         {
13289             tag: 'tr',
13290             cn: [
13291             {
13292                 tag: 'th',
13293                 cls: 'prev',
13294                 html: '<i class="icon-arrow-left"/>'
13295             },
13296             {
13297                 tag: 'th',
13298                 cls: 'switch',
13299                 colspan: '5'
13300             },
13301             {
13302                 tag: 'th',
13303                 cls: 'next',
13304                 html: '<i class="icon-arrow-right"/>'
13305             }
13306
13307             ]
13308         }
13309         ]
13310     },
13311     
13312     content : {
13313         tag: 'tbody',
13314         cn: [
13315         {
13316             tag: 'tr',
13317             cn: [
13318             {
13319                 tag: 'td',
13320                 colspan: '7'
13321             }
13322             ]
13323         }
13324         ]
13325     },
13326     
13327     footer : {
13328         tag: 'tfoot',
13329         cn: [
13330         {
13331             tag: 'tr',
13332             cn: [
13333             {
13334                 tag: 'th',
13335                 colspan: '7',
13336                 cls: 'today'
13337             }
13338                     
13339             ]
13340         }
13341         ]
13342     },
13343     
13344     dates:{
13345         en: {
13346             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
13347             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
13348             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
13349             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
13350             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
13351             today: "Today"
13352         }
13353     },
13354     
13355     modes: [
13356     {
13357         clsName: 'days',
13358         navFnc: 'Month',
13359         navStep: 1
13360     },
13361     {
13362         clsName: 'months',
13363         navFnc: 'FullYear',
13364         navStep: 1
13365     },
13366     {
13367         clsName: 'years',
13368         navFnc: 'FullYear',
13369         navStep: 10
13370     }]
13371 });
13372
13373 Roo.apply(Roo.bootstrap.DateField,  {
13374   
13375     template : {
13376         tag: 'div',
13377         cls: 'datepicker dropdown-menu',
13378         cn: [
13379         {
13380             tag: 'div',
13381             cls: 'datepicker-days',
13382             cn: [
13383             {
13384                 tag: 'table',
13385                 cls: 'table-condensed',
13386                 cn:[
13387                 Roo.bootstrap.DateField.head,
13388                 {
13389                     tag: 'tbody'
13390                 },
13391                 Roo.bootstrap.DateField.footer
13392                 ]
13393             }
13394             ]
13395         },
13396         {
13397             tag: 'div',
13398             cls: 'datepicker-months',
13399             cn: [
13400             {
13401                 tag: 'table',
13402                 cls: 'table-condensed',
13403                 cn:[
13404                 Roo.bootstrap.DateField.head,
13405                 Roo.bootstrap.DateField.content,
13406                 Roo.bootstrap.DateField.footer
13407                 ]
13408             }
13409             ]
13410         },
13411         {
13412             tag: 'div',
13413             cls: 'datepicker-years',
13414             cn: [
13415             {
13416                 tag: 'table',
13417                 cls: 'table-condensed',
13418                 cn:[
13419                 Roo.bootstrap.DateField.head,
13420                 Roo.bootstrap.DateField.content,
13421                 Roo.bootstrap.DateField.footer
13422                 ]
13423             }
13424             ]
13425         }
13426         ]
13427     }
13428 });
13429
13430  
13431
13432  /*
13433  * - LGPL
13434  *
13435  * TimeField
13436  * 
13437  */
13438
13439 /**
13440  * @class Roo.bootstrap.TimeField
13441  * @extends Roo.bootstrap.Input
13442  * Bootstrap DateField class
13443  * 
13444  * 
13445  * @constructor
13446  * Create a new TimeField
13447  * @param {Object} config The config object
13448  */
13449
13450 Roo.bootstrap.TimeField = function(config){
13451     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
13452     this.addEvents({
13453             /**
13454              * @event show
13455              * Fires when this field show.
13456              * @param {Roo.bootstrap.DateField} this
13457              * @param {Mixed} date The date value
13458              */
13459             show : true,
13460             /**
13461              * @event show
13462              * Fires when this field hide.
13463              * @param {Roo.bootstrap.DateField} this
13464              * @param {Mixed} date The date value
13465              */
13466             hide : true,
13467             /**
13468              * @event select
13469              * Fires when select a date.
13470              * @param {Roo.bootstrap.DateField} this
13471              * @param {Mixed} date The date value
13472              */
13473             select : true
13474         });
13475 };
13476
13477 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
13478     
13479     /**
13480      * @cfg {String} format
13481      * The default time format string which can be overriden for localization support.  The format must be
13482      * valid according to {@link Date#parseDate} (defaults to 'H:i').
13483      */
13484     format : "H:i",
13485        
13486     onRender: function(ct, position)
13487     {
13488         
13489         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
13490                 
13491         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
13492         
13493         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13494         
13495         this.pop = this.picker().select('>.datepicker-time',true).first();
13496         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block' 
13497         
13498         this.picker().on('mousedown', this.onMousedown, this);
13499         this.picker().on('click', this.onClick, this);
13500         
13501         this.picker().addClass('datepicker-dropdown');
13502     
13503         this.fillTime();
13504         this.update();
13505             
13506         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
13507         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
13508         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
13509         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
13510         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
13511         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
13512
13513     },
13514     
13515     fireKey: function(e){
13516         if (!this.picker().isVisible()){
13517             if (e.keyCode == 27) // allow escape to hide and re-show picker
13518                 this.show();
13519             return;
13520         }
13521
13522         e.preventDefault();
13523         
13524         switch(e.keyCode){
13525             case 27: // escape
13526                 this.hide();
13527                 break;
13528             case 37: // left
13529             case 39: // right
13530                 this.onTogglePeriod();
13531                 break;
13532             case 38: // up
13533                 this.onIncrementMinutes();
13534                 break;
13535             case 40: // down
13536                 this.onDecrementMinutes();
13537                 break;
13538             case 13: // enter
13539             case 9: // tab
13540                 this.setTime();
13541                 break;
13542         }
13543     },
13544     
13545     onClick: function(e) {
13546         e.stopPropagation();
13547         e.preventDefault();
13548     },
13549     
13550     picker : function()
13551     {
13552         return this.el.select('.datepicker', true).first();
13553     },
13554     
13555     fillTime: function()
13556     {    
13557         var time = this.pop.select('tbody', true).first();
13558         
13559         time.dom.innerHTML = '';
13560         
13561         time.createChild({
13562             tag: 'tr',
13563             cn: [
13564                 {
13565                     tag: 'td',
13566                     cn: [
13567                         {
13568                             tag: 'a',
13569                             href: '#',
13570                             cls: 'btn',
13571                             cn: [
13572                                 {
13573                                     tag: 'span',
13574                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
13575                                 }
13576                             ]
13577                         } 
13578                     ]
13579                 },
13580                 {
13581                     tag: 'td',
13582                     cls: 'separator'
13583                 },
13584                 {
13585                     tag: 'td',
13586                     cn: [
13587                         {
13588                             tag: 'a',
13589                             href: '#',
13590                             cls: 'btn',
13591                             cn: [
13592                                 {
13593                                     tag: 'span',
13594                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
13595                                 }
13596                             ]
13597                         }
13598                     ]
13599                 },
13600                 {
13601                     tag: 'td',
13602                     cls: 'separator'
13603                 }
13604             ]
13605         });
13606         
13607         time.createChild({
13608             tag: 'tr',
13609             cn: [
13610                 {
13611                     tag: 'td',
13612                     cn: [
13613                         {
13614                             tag: 'span',
13615                             cls: 'timepicker-hour',
13616                             html: '00'
13617                         }  
13618                     ]
13619                 },
13620                 {
13621                     tag: 'td',
13622                     cls: 'separator',
13623                     html: ':'
13624                 },
13625                 {
13626                     tag: 'td',
13627                     cn: [
13628                         {
13629                             tag: 'span',
13630                             cls: 'timepicker-minute',
13631                             html: '00'
13632                         }  
13633                     ]
13634                 },
13635                 {
13636                     tag: 'td',
13637                     cls: 'separator'
13638                 },
13639                 {
13640                     tag: 'td',
13641                     cn: [
13642                         {
13643                             tag: 'button',
13644                             type: 'button',
13645                             cls: 'btn btn-primary period',
13646                             html: 'AM'
13647                             
13648                         }
13649                     ]
13650                 }
13651             ]
13652         });
13653         
13654         time.createChild({
13655             tag: 'tr',
13656             cn: [
13657                 {
13658                     tag: 'td',
13659                     cn: [
13660                         {
13661                             tag: 'a',
13662                             href: '#',
13663                             cls: 'btn',
13664                             cn: [
13665                                 {
13666                                     tag: 'span',
13667                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
13668                                 }
13669                             ]
13670                         }
13671                     ]
13672                 },
13673                 {
13674                     tag: 'td',
13675                     cls: 'separator'
13676                 },
13677                 {
13678                     tag: 'td',
13679                     cn: [
13680                         {
13681                             tag: 'a',
13682                             href: '#',
13683                             cls: 'btn',
13684                             cn: [
13685                                 {
13686                                     tag: 'span',
13687                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
13688                                 }
13689                             ]
13690                         }
13691                     ]
13692                 },
13693                 {
13694                     tag: 'td',
13695                     cls: 'separator'
13696                 }
13697             ]
13698         });
13699         
13700     },
13701     
13702     update: function()
13703     {
13704         
13705         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
13706         
13707         this.fill();
13708     },
13709     
13710     fill: function() 
13711     {
13712         var hours = this.time.getHours();
13713         var minutes = this.time.getMinutes();
13714         var period = 'AM';
13715         
13716         if(hours > 11){
13717             period = 'PM';
13718         }
13719         
13720         if(hours == 0){
13721             hours = 12;
13722         }
13723         
13724         
13725         if(hours > 12){
13726             hours = hours - 12;
13727         }
13728         
13729         if(hours < 10){
13730             hours = '0' + hours;
13731         }
13732         
13733         if(minutes < 10){
13734             minutes = '0' + minutes;
13735         }
13736         
13737         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
13738         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
13739         this.pop.select('button', true).first().dom.innerHTML = period;
13740         
13741     },
13742     
13743     place: function()
13744     {   
13745         this.picker().removeClass(['bottom', 'top']);
13746         
13747         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
13748             /*
13749              * place to the top of element!
13750              *
13751              */
13752             
13753             this.picker().addClass('top');
13754             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
13755             
13756             return;
13757         }
13758         
13759         this.picker().addClass('bottom');
13760         
13761         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
13762     },
13763   
13764     onFocus : function()
13765     {
13766         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
13767         this.show();
13768     },
13769     
13770     onBlur : function()
13771     {
13772         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
13773         this.hide();
13774     },
13775     
13776     show : function()
13777     {
13778         this.picker().show();
13779         this.pop.show();
13780         this.update();
13781         this.place();
13782         
13783         this.fireEvent('show', this, this.date);
13784     },
13785     
13786     hide : function()
13787     {
13788         this.picker().hide();
13789         this.pop.hide();
13790         
13791         this.fireEvent('hide', this, this.date);
13792     },
13793     
13794     setTime : function()
13795     {
13796         this.hide();
13797         this.setValue(this.time.format(this.format));
13798         
13799         this.fireEvent('select', this, this.date);
13800         
13801         
13802     },
13803     
13804     onMousedown: function(e){
13805         e.stopPropagation();
13806         e.preventDefault();
13807     },
13808     
13809     onIncrementHours: function()
13810     {
13811         Roo.log('onIncrementHours');
13812         this.time = this.time.add(Date.HOUR, 1);
13813         this.update();
13814         
13815     },
13816     
13817     onDecrementHours: function()
13818     {
13819         Roo.log('onDecrementHours');
13820         this.time = this.time.add(Date.HOUR, -1);
13821         this.update();
13822     },
13823     
13824     onIncrementMinutes: function()
13825     {
13826         Roo.log('onIncrementMinutes');
13827         this.time = this.time.add(Date.MINUTE, 1);
13828         this.update();
13829     },
13830     
13831     onDecrementMinutes: function()
13832     {
13833         Roo.log('onDecrementMinutes');
13834         this.time = this.time.add(Date.MINUTE, -1);
13835         this.update();
13836     },
13837     
13838     onTogglePeriod: function()
13839     {
13840         Roo.log('onTogglePeriod');
13841         this.time = this.time.add(Date.HOUR, 12);
13842         this.update();
13843     }
13844     
13845    
13846 });
13847
13848 Roo.apply(Roo.bootstrap.TimeField,  {
13849     
13850     content : {
13851         tag: 'tbody',
13852         cn: [
13853             {
13854                 tag: 'tr',
13855                 cn: [
13856                 {
13857                     tag: 'td',
13858                     colspan: '7'
13859                 }
13860                 ]
13861             }
13862         ]
13863     },
13864     
13865     footer : {
13866         tag: 'tfoot',
13867         cn: [
13868             {
13869                 tag: 'tr',
13870                 cn: [
13871                 {
13872                     tag: 'th',
13873                     colspan: '7',
13874                     cls: '',
13875                     cn: [
13876                         {
13877                             tag: 'button',
13878                             cls: 'btn btn-info ok',
13879                             html: 'OK'
13880                         }
13881                     ]
13882                 }
13883
13884                 ]
13885             }
13886         ]
13887     }
13888 });
13889
13890 Roo.apply(Roo.bootstrap.TimeField,  {
13891   
13892     template : {
13893         tag: 'div',
13894         cls: 'datepicker dropdown-menu',
13895         cn: [
13896             {
13897                 tag: 'div',
13898                 cls: 'datepicker-time',
13899                 cn: [
13900                 {
13901                     tag: 'table',
13902                     cls: 'table-condensed',
13903                     cn:[
13904                     Roo.bootstrap.TimeField.content,
13905                     Roo.bootstrap.TimeField.footer
13906                     ]
13907                 }
13908                 ]
13909             }
13910         ]
13911     }
13912 });
13913
13914  
13915
13916  /*
13917  * - LGPL
13918  *
13919  * CheckBox
13920  * 
13921  */
13922
13923 /**
13924  * @class Roo.bootstrap.CheckBox
13925  * @extends Roo.bootstrap.Input
13926  * Bootstrap CheckBox class
13927  * 
13928  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
13929  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
13930  * @cfg {String} boxLabel The text that appears beside the checkbox
13931  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
13932  * @cfg {Boolean} checked initnal the element
13933  * 
13934  * 
13935  * @constructor
13936  * Create a new CheckBox
13937  * @param {Object} config The config object
13938  */
13939
13940 Roo.bootstrap.CheckBox = function(config){
13941     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
13942    
13943         this.addEvents({
13944             /**
13945             * @event check
13946             * Fires when the element is checked or unchecked.
13947             * @param {Roo.bootstrap.CheckBox} this This input
13948             * @param {Boolean} checked The new checked value
13949             */
13950            check : true
13951         });
13952 };
13953
13954 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
13955     
13956     inputType: 'checkbox',
13957     inputValue: 1,
13958     valueOff: 0,
13959     boxLabel: false,
13960     checked: false,
13961     weight : false,
13962     
13963     getAutoCreate : function()
13964     {
13965         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
13966         
13967         var id = Roo.id();
13968         
13969         var cfg = {};
13970         
13971         cfg.cls = 'form-group checkbox' //input-group
13972         
13973         
13974         
13975         
13976         var input =  {
13977             tag: 'input',
13978             id : id,
13979             type : this.inputType,
13980             value : (!this.checked) ? this.valueOff : this.inputValue,
13981             cls : 'roo-checkbox', //'form-box',
13982             placeholder : this.placeholder || ''
13983             
13984         };
13985         
13986         if (this.weight) { // Validity check?
13987             cfg.cls += " checkbox-" + this.weight;
13988         }
13989         
13990         if (this.disabled) {
13991             input.disabled=true;
13992         }
13993         
13994         if(this.checked){
13995             input.checked = this.checked;
13996         }
13997         
13998         if (this.name) {
13999             input.name = this.name;
14000         }
14001         
14002         if (this.size) {
14003             input.cls += ' input-' + this.size;
14004         }
14005         
14006         var settings=this;
14007         ['xs','sm','md','lg'].map(function(size){
14008             if (settings[size]) {
14009                 cfg.cls += ' col-' + size + '-' + settings[size];
14010             }
14011         });
14012         
14013        
14014         
14015         var inputblock = input;
14016         
14017         
14018         
14019         
14020         if (this.before || this.after) {
14021             
14022             inputblock = {
14023                 cls : 'input-group',
14024                 cn :  [] 
14025             };
14026             if (this.before) {
14027                 inputblock.cn.push({
14028                     tag :'span',
14029                     cls : 'input-group-addon',
14030                     html : this.before
14031                 });
14032             }
14033             inputblock.cn.push(input);
14034             if (this.after) {
14035                 inputblock.cn.push({
14036                     tag :'span',
14037                     cls : 'input-group-addon',
14038                     html : this.after
14039                 });
14040             }
14041             
14042         };
14043         
14044         if (align ==='left' && this.fieldLabel.length) {
14045                 Roo.log("left and has label");
14046                 cfg.cn = [
14047                     
14048                     {
14049                         tag: 'label',
14050                         'for' :  id,
14051                         cls : 'control-label col-md-' + this.labelWidth,
14052                         html : this.fieldLabel
14053                         
14054                     },
14055                     {
14056                         cls : "col-md-" + (12 - this.labelWidth), 
14057                         cn: [
14058                             inputblock
14059                         ]
14060                     }
14061                     
14062                 ];
14063         } else if ( this.fieldLabel.length) {
14064                 Roo.log(" label");
14065                 cfg.cn = [
14066                    
14067                     {
14068                         tag: this.boxLabel ? 'span' : 'label',
14069                         'for': id,
14070                         cls: 'control-label box-input-label',
14071                         //cls : 'input-group-addon',
14072                         html : this.fieldLabel
14073                         
14074                     },
14075                     
14076                     inputblock
14077                     
14078                 ];
14079
14080         } else {
14081             
14082                 Roo.log(" no label && no align");
14083                 cfg.cn = [  inputblock ] ;
14084                 
14085                 
14086         };
14087          if(this.boxLabel){
14088             cfg.cn.push( {
14089                 tag: 'label',
14090                 'for': id,
14091                 cls: 'box-label',
14092                 html: this.boxLabel
14093                 
14094             });
14095         }
14096         
14097         
14098        
14099         return cfg;
14100         
14101     },
14102     
14103     /**
14104      * return the real input element.
14105      */
14106     inputEl: function ()
14107     {
14108         return this.el.select('input.roo-checkbox',true).first();
14109     },
14110     
14111     label: function()
14112     {
14113         return this.el.select('label.control-label',true).first();
14114     },
14115     
14116     initEvents : function()
14117     {
14118 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
14119         
14120         this.inputEl().on('click', this.onClick,  this);
14121         
14122     },
14123     
14124     onClick : function()
14125     {   
14126         this.setChecked(!this.checked);
14127     },
14128     
14129     setChecked : function(state,suppressEvent)
14130     {
14131         this.checked = state;
14132         
14133         this.inputEl().dom.checked = state;
14134         
14135         if(suppressEvent !== true){
14136             this.fireEvent('check', this, state);
14137         }
14138         
14139         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
14140         
14141     },
14142     
14143     setValue : function(v,suppressEvent)
14144     {
14145         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
14146     }
14147     
14148 });
14149
14150  
14151 /*
14152  * - LGPL
14153  *
14154  * Radio
14155  * 
14156  */
14157
14158 /**
14159  * @class Roo.bootstrap.Radio
14160  * @extends Roo.bootstrap.CheckBox
14161  * Bootstrap Radio class
14162
14163  * @constructor
14164  * Create a new Radio
14165  * @param {Object} config The config object
14166  */
14167
14168 Roo.bootstrap.Radio = function(config){
14169     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
14170    
14171 };
14172
14173 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
14174     
14175     inputType: 'radio',
14176     inputValue: '',
14177     valueOff: '',
14178     
14179     getAutoCreate : function()
14180     {
14181         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
14182         
14183         var id = Roo.id();
14184         
14185         var cfg = {};
14186         
14187         cfg.cls = 'form-group radio' //input-group
14188         
14189         var input =  {
14190             tag: 'input',
14191             id : id,
14192             type : this.inputType,
14193             value : (!this.checked) ? this.valueOff : this.inputValue,
14194             cls : 'roo-radio',
14195             placeholder : this.placeholder || ''
14196             
14197         };
14198           if (this.weight) { // Validity check?
14199             cfg.cls += " radio-" + this.weight;
14200         }
14201         if (this.disabled) {
14202             input.disabled=true;
14203         }
14204         
14205         if(this.checked){
14206             input.checked = this.checked;
14207         }
14208         
14209         if (this.name) {
14210             input.name = this.name;
14211         }
14212         
14213         if (this.size) {
14214             input.cls += ' input-' + this.size;
14215         }
14216         
14217         var settings=this;
14218         ['xs','sm','md','lg'].map(function(size){
14219             if (settings[size]) {
14220                 cfg.cls += ' col-' + size + '-' + settings[size];
14221             }
14222         });
14223         
14224         var inputblock = input;
14225         
14226         if (this.before || this.after) {
14227             
14228             inputblock = {
14229                 cls : 'input-group',
14230                 cn :  [] 
14231             };
14232             if (this.before) {
14233                 inputblock.cn.push({
14234                     tag :'span',
14235                     cls : 'input-group-addon',
14236                     html : this.before
14237                 });
14238             }
14239             inputblock.cn.push(input);
14240             if (this.after) {
14241                 inputblock.cn.push({
14242                     tag :'span',
14243                     cls : 'input-group-addon',
14244                     html : this.after
14245                 });
14246             }
14247             
14248         };
14249         
14250         if (align ==='left' && this.fieldLabel.length) {
14251                 Roo.log("left and has label");
14252                 cfg.cn = [
14253                     
14254                     {
14255                         tag: 'label',
14256                         'for' :  id,
14257                         cls : 'control-label col-md-' + this.labelWidth,
14258                         html : this.fieldLabel
14259                         
14260                     },
14261                     {
14262                         cls : "col-md-" + (12 - this.labelWidth), 
14263                         cn: [
14264                             inputblock
14265                         ]
14266                     }
14267                     
14268                 ];
14269         } else if ( this.fieldLabel.length) {
14270                 Roo.log(" label");
14271                  cfg.cn = [
14272                    
14273                     {
14274                         tag: 'label',
14275                         'for': id,
14276                         cls: 'control-label box-input-label',
14277                         //cls : 'input-group-addon',
14278                         html : this.fieldLabel
14279                         
14280                     },
14281                     
14282                     inputblock
14283                     
14284                 ];
14285
14286         } else {
14287             
14288                    Roo.log(" no label && no align");
14289                 cfg.cn = [
14290                     
14291                         inputblock
14292                     
14293                 ];
14294                 
14295                 
14296         };
14297         
14298         if(this.boxLabel){
14299             cfg.cn.push({
14300                 tag: 'label',
14301                 'for': id,
14302                 cls: 'box-label',
14303                 html: this.boxLabel
14304             })
14305         }
14306         
14307         return cfg;
14308         
14309     },
14310     inputEl: function ()
14311     {
14312         return this.el.select('input.roo-radio',true).first();
14313     },
14314     onClick : function()
14315     {   
14316         this.setChecked(true);
14317     },
14318     
14319     setChecked : function(state,suppressEvent)
14320     {
14321         if(state){
14322             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
14323                 v.dom.checked = false;
14324             });
14325         }
14326         
14327         this.checked = state;
14328         this.inputEl().dom.checked = state;
14329         
14330         if(suppressEvent !== true){
14331             this.fireEvent('check', this, state);
14332         }
14333         
14334         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
14335         
14336     },
14337     
14338     getGroupValue : function()
14339     {
14340         var value = ''
14341         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
14342             if(v.dom.checked == true){
14343                 value = v.dom.value;
14344             }
14345         });
14346         
14347         return value;
14348     },
14349     
14350     /**
14351      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
14352      * @return {Mixed} value The field value
14353      */
14354     getValue : function(){
14355         return this.getGroupValue();
14356     }
14357     
14358 });
14359
14360  
14361 //<script type="text/javascript">
14362
14363 /*
14364  * Based  Ext JS Library 1.1.1
14365  * Copyright(c) 2006-2007, Ext JS, LLC.
14366  * LGPL
14367  *
14368  */
14369  
14370 /**
14371  * @class Roo.HtmlEditorCore
14372  * @extends Roo.Component
14373  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
14374  *
14375  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
14376  */
14377
14378 Roo.HtmlEditorCore = function(config){
14379     
14380     
14381     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
14382     this.addEvents({
14383         /**
14384          * @event initialize
14385          * Fires when the editor is fully initialized (including the iframe)
14386          * @param {Roo.HtmlEditorCore} this
14387          */
14388         initialize: true,
14389         /**
14390          * @event activate
14391          * Fires when the editor is first receives the focus. Any insertion must wait
14392          * until after this event.
14393          * @param {Roo.HtmlEditorCore} this
14394          */
14395         activate: true,
14396          /**
14397          * @event beforesync
14398          * Fires before the textarea is updated with content from the editor iframe. Return false
14399          * to cancel the sync.
14400          * @param {Roo.HtmlEditorCore} this
14401          * @param {String} html
14402          */
14403         beforesync: true,
14404          /**
14405          * @event beforepush
14406          * Fires before the iframe editor is updated with content from the textarea. Return false
14407          * to cancel the push.
14408          * @param {Roo.HtmlEditorCore} this
14409          * @param {String} html
14410          */
14411         beforepush: true,
14412          /**
14413          * @event sync
14414          * Fires when the textarea is updated with content from the editor iframe.
14415          * @param {Roo.HtmlEditorCore} this
14416          * @param {String} html
14417          */
14418         sync: true,
14419          /**
14420          * @event push
14421          * Fires when the iframe editor is updated with content from the textarea.
14422          * @param {Roo.HtmlEditorCore} this
14423          * @param {String} html
14424          */
14425         push: true,
14426         
14427         /**
14428          * @event editorevent
14429          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
14430          * @param {Roo.HtmlEditorCore} this
14431          */
14432         editorevent: true
14433     });
14434      
14435 };
14436
14437
14438 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
14439
14440
14441      /**
14442      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
14443      */
14444     
14445     owner : false,
14446     
14447      /**
14448      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
14449      *                        Roo.resizable.
14450      */
14451     resizable : false,
14452      /**
14453      * @cfg {Number} height (in pixels)
14454      */   
14455     height: 300,
14456    /**
14457      * @cfg {Number} width (in pixels)
14458      */   
14459     width: 500,
14460     
14461     /**
14462      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
14463      * 
14464      */
14465     stylesheets: false,
14466     
14467     // id of frame..
14468     frameId: false,
14469     
14470     // private properties
14471     validationEvent : false,
14472     deferHeight: true,
14473     initialized : false,
14474     activated : false,
14475     sourceEditMode : false,
14476     onFocus : Roo.emptyFn,
14477     iframePad:3,
14478     hideMode:'offsets',
14479     
14480     clearUp: true,
14481     
14482      
14483     
14484
14485     /**
14486      * Protected method that will not generally be called directly. It
14487      * is called when the editor initializes the iframe with HTML contents. Override this method if you
14488      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
14489      */
14490     getDocMarkup : function(){
14491         // body styles..
14492         var st = '';
14493         Roo.log(this.stylesheets);
14494         
14495         // inherit styels from page...?? 
14496         if (this.stylesheets === false) {
14497             
14498             Roo.get(document.head).select('style').each(function(node) {
14499                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
14500             });
14501             
14502             Roo.get(document.head).select('link').each(function(node) { 
14503                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
14504             });
14505             
14506         } else if (!this.stylesheets.length) {
14507                 // simple..
14508                 st = '<style type="text/css">' +
14509                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
14510                    '</style>';
14511         } else {
14512             Roo.each(this.stylesheets, function(s) {
14513                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
14514             });
14515             
14516         }
14517         
14518         st +=  '<style type="text/css">' +
14519             'IMG { cursor: pointer } ' +
14520         '</style>';
14521
14522         
14523         return '<html><head>' + st  +
14524             //<style type="text/css">' +
14525             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
14526             //'</style>' +
14527             ' </head><body class="roo-htmleditor-body"></body></html>';
14528     },
14529
14530     // private
14531     onRender : function(ct, position)
14532     {
14533         var _t = this;
14534         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
14535         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
14536         
14537         
14538         this.el.dom.style.border = '0 none';
14539         this.el.dom.setAttribute('tabIndex', -1);
14540         this.el.addClass('x-hidden hide');
14541         
14542         
14543         
14544         if(Roo.isIE){ // fix IE 1px bogus margin
14545             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
14546         }
14547        
14548         
14549         this.frameId = Roo.id();
14550         
14551          
14552         
14553         var iframe = this.owner.wrap.createChild({
14554             tag: 'iframe',
14555             cls: 'form-control', // bootstrap..
14556             id: this.frameId,
14557             name: this.frameId,
14558             frameBorder : 'no',
14559             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
14560         }, this.el
14561         );
14562         
14563         
14564         this.iframe = iframe.dom;
14565
14566          this.assignDocWin();
14567         
14568         this.doc.designMode = 'on';
14569        
14570         this.doc.open();
14571         this.doc.write(this.getDocMarkup());
14572         this.doc.close();
14573
14574         
14575         var task = { // must defer to wait for browser to be ready
14576             run : function(){
14577                 //console.log("run task?" + this.doc.readyState);
14578                 this.assignDocWin();
14579                 if(this.doc.body || this.doc.readyState == 'complete'){
14580                     try {
14581                         this.doc.designMode="on";
14582                     } catch (e) {
14583                         return;
14584                     }
14585                     Roo.TaskMgr.stop(task);
14586                     this.initEditor.defer(10, this);
14587                 }
14588             },
14589             interval : 10,
14590             duration: 10000,
14591             scope: this
14592         };
14593         Roo.TaskMgr.start(task);
14594
14595         
14596          
14597     },
14598
14599     // private
14600     onResize : function(w, h)
14601     {
14602          Roo.log('resize: ' +w + ',' + h );
14603         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
14604         if(!this.iframe){
14605             return;
14606         }
14607         if(typeof w == 'number'){
14608             
14609             this.iframe.style.width = w + 'px';
14610         }
14611         if(typeof h == 'number'){
14612             
14613             this.iframe.style.height = h + 'px';
14614             if(this.doc){
14615                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
14616             }
14617         }
14618         
14619     },
14620
14621     /**
14622      * Toggles the editor between standard and source edit mode.
14623      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
14624      */
14625     toggleSourceEdit : function(sourceEditMode){
14626         
14627         this.sourceEditMode = sourceEditMode === true;
14628         
14629         if(this.sourceEditMode){
14630  
14631             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
14632             
14633         }else{
14634             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
14635             //this.iframe.className = '';
14636             this.deferFocus();
14637         }
14638         //this.setSize(this.owner.wrap.getSize());
14639         //this.fireEvent('editmodechange', this, this.sourceEditMode);
14640     },
14641
14642     
14643   
14644
14645     /**
14646      * Protected method that will not generally be called directly. If you need/want
14647      * custom HTML cleanup, this is the method you should override.
14648      * @param {String} html The HTML to be cleaned
14649      * return {String} The cleaned HTML
14650      */
14651     cleanHtml : function(html){
14652         html = String(html);
14653         if(html.length > 5){
14654             if(Roo.isSafari){ // strip safari nonsense
14655                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
14656             }
14657         }
14658         if(html == '&nbsp;'){
14659             html = '';
14660         }
14661         return html;
14662     },
14663
14664     /**
14665      * HTML Editor -> Textarea
14666      * Protected method that will not generally be called directly. Syncs the contents
14667      * of the editor iframe with the textarea.
14668      */
14669     syncValue : function(){
14670         if(this.initialized){
14671             var bd = (this.doc.body || this.doc.documentElement);
14672             //this.cleanUpPaste(); -- this is done else where and causes havoc..
14673             var html = bd.innerHTML;
14674             if(Roo.isSafari){
14675                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
14676                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
14677                 if(m && m[1]){
14678                     html = '<div style="'+m[0]+'">' + html + '</div>';
14679                 }
14680             }
14681             html = this.cleanHtml(html);
14682             // fix up the special chars.. normaly like back quotes in word...
14683             // however we do not want to do this with chinese..
14684             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
14685                 var cc = b.charCodeAt();
14686                 if (
14687                     (cc >= 0x4E00 && cc < 0xA000 ) ||
14688                     (cc >= 0x3400 && cc < 0x4E00 ) ||
14689                     (cc >= 0xf900 && cc < 0xfb00 )
14690                 ) {
14691                         return b;
14692                 }
14693                 return "&#"+cc+";" 
14694             });
14695             if(this.owner.fireEvent('beforesync', this, html) !== false){
14696                 this.el.dom.value = html;
14697                 this.owner.fireEvent('sync', this, html);
14698             }
14699         }
14700     },
14701
14702     /**
14703      * Protected method that will not generally be called directly. Pushes the value of the textarea
14704      * into the iframe editor.
14705      */
14706     pushValue : function(){
14707         if(this.initialized){
14708             var v = this.el.dom.value.trim();
14709             
14710 //            if(v.length < 1){
14711 //                v = '&#160;';
14712 //            }
14713             
14714             if(this.owner.fireEvent('beforepush', this, v) !== false){
14715                 var d = (this.doc.body || this.doc.documentElement);
14716                 d.innerHTML = v;
14717                 this.cleanUpPaste();
14718                 this.el.dom.value = d.innerHTML;
14719                 this.owner.fireEvent('push', this, v);
14720             }
14721         }
14722     },
14723
14724     // private
14725     deferFocus : function(){
14726         this.focus.defer(10, this);
14727     },
14728
14729     // doc'ed in Field
14730     focus : function(){
14731         if(this.win && !this.sourceEditMode){
14732             this.win.focus();
14733         }else{
14734             this.el.focus();
14735         }
14736     },
14737     
14738     assignDocWin: function()
14739     {
14740         var iframe = this.iframe;
14741         
14742          if(Roo.isIE){
14743             this.doc = iframe.contentWindow.document;
14744             this.win = iframe.contentWindow;
14745         } else {
14746             if (!Roo.get(this.frameId)) {
14747                 return;
14748             }
14749             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
14750             this.win = Roo.get(this.frameId).dom.contentWindow;
14751         }
14752     },
14753     
14754     // private
14755     initEditor : function(){
14756         //console.log("INIT EDITOR");
14757         this.assignDocWin();
14758         
14759         
14760         
14761         this.doc.designMode="on";
14762         this.doc.open();
14763         this.doc.write(this.getDocMarkup());
14764         this.doc.close();
14765         
14766         var dbody = (this.doc.body || this.doc.documentElement);
14767         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
14768         // this copies styles from the containing element into thsi one..
14769         // not sure why we need all of this..
14770         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
14771         ss['background-attachment'] = 'fixed'; // w3c
14772         dbody.bgProperties = 'fixed'; // ie
14773         Roo.DomHelper.applyStyles(dbody, ss);
14774         Roo.EventManager.on(this.doc, {
14775             //'mousedown': this.onEditorEvent,
14776             'mouseup': this.onEditorEvent,
14777             'dblclick': this.onEditorEvent,
14778             'click': this.onEditorEvent,
14779             'keyup': this.onEditorEvent,
14780             buffer:100,
14781             scope: this
14782         });
14783         if(Roo.isGecko){
14784             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
14785         }
14786         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
14787             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
14788         }
14789         this.initialized = true;
14790
14791         this.owner.fireEvent('initialize', this);
14792         this.pushValue();
14793     },
14794
14795     // private
14796     onDestroy : function(){
14797         
14798         
14799         
14800         if(this.rendered){
14801             
14802             //for (var i =0; i < this.toolbars.length;i++) {
14803             //    // fixme - ask toolbars for heights?
14804             //    this.toolbars[i].onDestroy();
14805            // }
14806             
14807             //this.wrap.dom.innerHTML = '';
14808             //this.wrap.remove();
14809         }
14810     },
14811
14812     // private
14813     onFirstFocus : function(){
14814         
14815         this.assignDocWin();
14816         
14817         
14818         this.activated = true;
14819          
14820     
14821         if(Roo.isGecko){ // prevent silly gecko errors
14822             this.win.focus();
14823             var s = this.win.getSelection();
14824             if(!s.focusNode || s.focusNode.nodeType != 3){
14825                 var r = s.getRangeAt(0);
14826                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
14827                 r.collapse(true);
14828                 this.deferFocus();
14829             }
14830             try{
14831                 this.execCmd('useCSS', true);
14832                 this.execCmd('styleWithCSS', false);
14833             }catch(e){}
14834         }
14835         this.owner.fireEvent('activate', this);
14836     },
14837
14838     // private
14839     adjustFont: function(btn){
14840         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
14841         //if(Roo.isSafari){ // safari
14842         //    adjust *= 2;
14843        // }
14844         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
14845         if(Roo.isSafari){ // safari
14846             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
14847             v =  (v < 10) ? 10 : v;
14848             v =  (v > 48) ? 48 : v;
14849             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
14850             
14851         }
14852         
14853         
14854         v = Math.max(1, v+adjust);
14855         
14856         this.execCmd('FontSize', v  );
14857     },
14858
14859     onEditorEvent : function(e){
14860         this.owner.fireEvent('editorevent', this, e);
14861       //  this.updateToolbar();
14862         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
14863     },
14864
14865     insertTag : function(tg)
14866     {
14867         // could be a bit smarter... -> wrap the current selected tRoo..
14868         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
14869             
14870             range = this.createRange(this.getSelection());
14871             var wrappingNode = this.doc.createElement(tg.toLowerCase());
14872             wrappingNode.appendChild(range.extractContents());
14873             range.insertNode(wrappingNode);
14874
14875             return;
14876             
14877             
14878             
14879         }
14880         this.execCmd("formatblock",   tg);
14881         
14882     },
14883     
14884     insertText : function(txt)
14885     {
14886         
14887         
14888         var range = this.createRange();
14889         range.deleteContents();
14890                //alert(Sender.getAttribute('label'));
14891                
14892         range.insertNode(this.doc.createTextNode(txt));
14893     } ,
14894     
14895      
14896
14897     /**
14898      * Executes a Midas editor command on the editor document and performs necessary focus and
14899      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
14900      * @param {String} cmd The Midas command
14901      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
14902      */
14903     relayCmd : function(cmd, value){
14904         this.win.focus();
14905         this.execCmd(cmd, value);
14906         this.owner.fireEvent('editorevent', this);
14907         //this.updateToolbar();
14908         this.owner.deferFocus();
14909     },
14910
14911     /**
14912      * Executes a Midas editor command directly on the editor document.
14913      * For visual commands, you should use {@link #relayCmd} instead.
14914      * <b>This should only be called after the editor is initialized.</b>
14915      * @param {String} cmd The Midas command
14916      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
14917      */
14918     execCmd : function(cmd, value){
14919         this.doc.execCommand(cmd, false, value === undefined ? null : value);
14920         this.syncValue();
14921     },
14922  
14923  
14924    
14925     /**
14926      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
14927      * to insert tRoo.
14928      * @param {String} text | dom node.. 
14929      */
14930     insertAtCursor : function(text)
14931     {
14932         
14933         
14934         
14935         if(!this.activated){
14936             return;
14937         }
14938         /*
14939         if(Roo.isIE){
14940             this.win.focus();
14941             var r = this.doc.selection.createRange();
14942             if(r){
14943                 r.collapse(true);
14944                 r.pasteHTML(text);
14945                 this.syncValue();
14946                 this.deferFocus();
14947             
14948             }
14949             return;
14950         }
14951         */
14952         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
14953             this.win.focus();
14954             
14955             
14956             // from jquery ui (MIT licenced)
14957             var range, node;
14958             var win = this.win;
14959             
14960             if (win.getSelection && win.getSelection().getRangeAt) {
14961                 range = win.getSelection().getRangeAt(0);
14962                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
14963                 range.insertNode(node);
14964             } else if (win.document.selection && win.document.selection.createRange) {
14965                 // no firefox support
14966                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
14967                 win.document.selection.createRange().pasteHTML(txt);
14968             } else {
14969                 // no firefox support
14970                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
14971                 this.execCmd('InsertHTML', txt);
14972             } 
14973             
14974             this.syncValue();
14975             
14976             this.deferFocus();
14977         }
14978     },
14979  // private
14980     mozKeyPress : function(e){
14981         if(e.ctrlKey){
14982             var c = e.getCharCode(), cmd;
14983           
14984             if(c > 0){
14985                 c = String.fromCharCode(c).toLowerCase();
14986                 switch(c){
14987                     case 'b':
14988                         cmd = 'bold';
14989                         break;
14990                     case 'i':
14991                         cmd = 'italic';
14992                         break;
14993                     
14994                     case 'u':
14995                         cmd = 'underline';
14996                         break;
14997                     
14998                     case 'v':
14999                         this.cleanUpPaste.defer(100, this);
15000                         return;
15001                         
15002                 }
15003                 if(cmd){
15004                     this.win.focus();
15005                     this.execCmd(cmd);
15006                     this.deferFocus();
15007                     e.preventDefault();
15008                 }
15009                 
15010             }
15011         }
15012     },
15013
15014     // private
15015     fixKeys : function(){ // load time branching for fastest keydown performance
15016         if(Roo.isIE){
15017             return function(e){
15018                 var k = e.getKey(), r;
15019                 if(k == e.TAB){
15020                     e.stopEvent();
15021                     r = this.doc.selection.createRange();
15022                     if(r){
15023                         r.collapse(true);
15024                         r.pasteHTML('&#160;&#160;&#160;&#160;');
15025                         this.deferFocus();
15026                     }
15027                     return;
15028                 }
15029                 
15030                 if(k == e.ENTER){
15031                     r = this.doc.selection.createRange();
15032                     if(r){
15033                         var target = r.parentElement();
15034                         if(!target || target.tagName.toLowerCase() != 'li'){
15035                             e.stopEvent();
15036                             r.pasteHTML('<br />');
15037                             r.collapse(false);
15038                             r.select();
15039                         }
15040                     }
15041                 }
15042                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
15043                     this.cleanUpPaste.defer(100, this);
15044                     return;
15045                 }
15046                 
15047                 
15048             };
15049         }else if(Roo.isOpera){
15050             return function(e){
15051                 var k = e.getKey();
15052                 if(k == e.TAB){
15053                     e.stopEvent();
15054                     this.win.focus();
15055                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
15056                     this.deferFocus();
15057                 }
15058                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
15059                     this.cleanUpPaste.defer(100, this);
15060                     return;
15061                 }
15062                 
15063             };
15064         }else if(Roo.isSafari){
15065             return function(e){
15066                 var k = e.getKey();
15067                 
15068                 if(k == e.TAB){
15069                     e.stopEvent();
15070                     this.execCmd('InsertText','\t');
15071                     this.deferFocus();
15072                     return;
15073                 }
15074                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
15075                     this.cleanUpPaste.defer(100, this);
15076                     return;
15077                 }
15078                 
15079              };
15080         }
15081     }(),
15082     
15083     getAllAncestors: function()
15084     {
15085         var p = this.getSelectedNode();
15086         var a = [];
15087         if (!p) {
15088             a.push(p); // push blank onto stack..
15089             p = this.getParentElement();
15090         }
15091         
15092         
15093         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
15094             a.push(p);
15095             p = p.parentNode;
15096         }
15097         a.push(this.doc.body);
15098         return a;
15099     },
15100     lastSel : false,
15101     lastSelNode : false,
15102     
15103     
15104     getSelection : function() 
15105     {
15106         this.assignDocWin();
15107         return Roo.isIE ? this.doc.selection : this.win.getSelection();
15108     },
15109     
15110     getSelectedNode: function() 
15111     {
15112         // this may only work on Gecko!!!
15113         
15114         // should we cache this!!!!
15115         
15116         
15117         
15118          
15119         var range = this.createRange(this.getSelection()).cloneRange();
15120         
15121         if (Roo.isIE) {
15122             var parent = range.parentElement();
15123             while (true) {
15124                 var testRange = range.duplicate();
15125                 testRange.moveToElementText(parent);
15126                 if (testRange.inRange(range)) {
15127                     break;
15128                 }
15129                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
15130                     break;
15131                 }
15132                 parent = parent.parentElement;
15133             }
15134             return parent;
15135         }
15136         
15137         // is ancestor a text element.
15138         var ac =  range.commonAncestorContainer;
15139         if (ac.nodeType == 3) {
15140             ac = ac.parentNode;
15141         }
15142         
15143         var ar = ac.childNodes;
15144          
15145         var nodes = [];
15146         var other_nodes = [];
15147         var has_other_nodes = false;
15148         for (var i=0;i<ar.length;i++) {
15149             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
15150                 continue;
15151             }
15152             // fullly contained node.
15153             
15154             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
15155                 nodes.push(ar[i]);
15156                 continue;
15157             }
15158             
15159             // probably selected..
15160             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
15161                 other_nodes.push(ar[i]);
15162                 continue;
15163             }
15164             // outer..
15165             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
15166                 continue;
15167             }
15168             
15169             
15170             has_other_nodes = true;
15171         }
15172         if (!nodes.length && other_nodes.length) {
15173             nodes= other_nodes;
15174         }
15175         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
15176             return false;
15177         }
15178         
15179         return nodes[0];
15180     },
15181     createRange: function(sel)
15182     {
15183         // this has strange effects when using with 
15184         // top toolbar - not sure if it's a great idea.
15185         //this.editor.contentWindow.focus();
15186         if (typeof sel != "undefined") {
15187             try {
15188                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
15189             } catch(e) {
15190                 return this.doc.createRange();
15191             }
15192         } else {
15193             return this.doc.createRange();
15194         }
15195     },
15196     getParentElement: function()
15197     {
15198         
15199         this.assignDocWin();
15200         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
15201         
15202         var range = this.createRange(sel);
15203          
15204         try {
15205             var p = range.commonAncestorContainer;
15206             while (p.nodeType == 3) { // text node
15207                 p = p.parentNode;
15208             }
15209             return p;
15210         } catch (e) {
15211             return null;
15212         }
15213     
15214     },
15215     /***
15216      *
15217      * Range intersection.. the hard stuff...
15218      *  '-1' = before
15219      *  '0' = hits..
15220      *  '1' = after.
15221      *         [ -- selected range --- ]
15222      *   [fail]                        [fail]
15223      *
15224      *    basically..
15225      *      if end is before start or  hits it. fail.
15226      *      if start is after end or hits it fail.
15227      *
15228      *   if either hits (but other is outside. - then it's not 
15229      *   
15230      *    
15231      **/
15232     
15233     
15234     // @see http://www.thismuchiknow.co.uk/?p=64.
15235     rangeIntersectsNode : function(range, node)
15236     {
15237         var nodeRange = node.ownerDocument.createRange();
15238         try {
15239             nodeRange.selectNode(node);
15240         } catch (e) {
15241             nodeRange.selectNodeContents(node);
15242         }
15243     
15244         var rangeStartRange = range.cloneRange();
15245         rangeStartRange.collapse(true);
15246     
15247         var rangeEndRange = range.cloneRange();
15248         rangeEndRange.collapse(false);
15249     
15250         var nodeStartRange = nodeRange.cloneRange();
15251         nodeStartRange.collapse(true);
15252     
15253         var nodeEndRange = nodeRange.cloneRange();
15254         nodeEndRange.collapse(false);
15255     
15256         return rangeStartRange.compareBoundaryPoints(
15257                  Range.START_TO_START, nodeEndRange) == -1 &&
15258                rangeEndRange.compareBoundaryPoints(
15259                  Range.START_TO_START, nodeStartRange) == 1;
15260         
15261          
15262     },
15263     rangeCompareNode : function(range, node)
15264     {
15265         var nodeRange = node.ownerDocument.createRange();
15266         try {
15267             nodeRange.selectNode(node);
15268         } catch (e) {
15269             nodeRange.selectNodeContents(node);
15270         }
15271         
15272         
15273         range.collapse(true);
15274     
15275         nodeRange.collapse(true);
15276      
15277         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
15278         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
15279          
15280         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
15281         
15282         var nodeIsBefore   =  ss == 1;
15283         var nodeIsAfter    = ee == -1;
15284         
15285         if (nodeIsBefore && nodeIsAfter)
15286             return 0; // outer
15287         if (!nodeIsBefore && nodeIsAfter)
15288             return 1; //right trailed.
15289         
15290         if (nodeIsBefore && !nodeIsAfter)
15291             return 2;  // left trailed.
15292         // fully contined.
15293         return 3;
15294     },
15295
15296     // private? - in a new class?
15297     cleanUpPaste :  function()
15298     {
15299         // cleans up the whole document..
15300         Roo.log('cleanuppaste');
15301         
15302         this.cleanUpChildren(this.doc.body);
15303         var clean = this.cleanWordChars(this.doc.body.innerHTML);
15304         if (clean != this.doc.body.innerHTML) {
15305             this.doc.body.innerHTML = clean;
15306         }
15307         
15308     },
15309     
15310     cleanWordChars : function(input) {// change the chars to hex code
15311         var he = Roo.HtmlEditorCore;
15312         
15313         var output = input;
15314         Roo.each(he.swapCodes, function(sw) { 
15315             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
15316             
15317             output = output.replace(swapper, sw[1]);
15318         });
15319         
15320         return output;
15321     },
15322     
15323     
15324     cleanUpChildren : function (n)
15325     {
15326         if (!n.childNodes.length) {
15327             return;
15328         }
15329         for (var i = n.childNodes.length-1; i > -1 ; i--) {
15330            this.cleanUpChild(n.childNodes[i]);
15331         }
15332     },
15333     
15334     
15335         
15336     
15337     cleanUpChild : function (node)
15338     {
15339         var ed = this;
15340         //console.log(node);
15341         if (node.nodeName == "#text") {
15342             // clean up silly Windows -- stuff?
15343             return; 
15344         }
15345         if (node.nodeName == "#comment") {
15346             node.parentNode.removeChild(node);
15347             // clean up silly Windows -- stuff?
15348             return; 
15349         }
15350         
15351         if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1 && this.clearUp) {
15352             // remove node.
15353             node.parentNode.removeChild(node);
15354             return;
15355             
15356         }
15357         
15358         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
15359         
15360         // remove <a name=....> as rendering on yahoo mailer is borked with this.
15361         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
15362         
15363         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
15364         //    remove_keep_children = true;
15365         //}
15366         
15367         if (remove_keep_children) {
15368             this.cleanUpChildren(node);
15369             // inserts everything just before this node...
15370             while (node.childNodes.length) {
15371                 var cn = node.childNodes[0];
15372                 node.removeChild(cn);
15373                 node.parentNode.insertBefore(cn, node);
15374             }
15375             node.parentNode.removeChild(node);
15376             return;
15377         }
15378         
15379         if (!node.attributes || !node.attributes.length) {
15380             this.cleanUpChildren(node);
15381             return;
15382         }
15383         
15384         function cleanAttr(n,v)
15385         {
15386             
15387             if (v.match(/^\./) || v.match(/^\//)) {
15388                 return;
15389             }
15390             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
15391                 return;
15392             }
15393             if (v.match(/^#/)) {
15394                 return;
15395             }
15396 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
15397             node.removeAttribute(n);
15398             
15399         }
15400         
15401         function cleanStyle(n,v)
15402         {
15403             if (v.match(/expression/)) { //XSS?? should we even bother..
15404                 node.removeAttribute(n);
15405                 return;
15406             }
15407             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
15408             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
15409             
15410             
15411             var parts = v.split(/;/);
15412             var clean = [];
15413             
15414             Roo.each(parts, function(p) {
15415                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
15416                 if (!p.length) {
15417                     return true;
15418                 }
15419                 var l = p.split(':').shift().replace(/\s+/g,'');
15420                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
15421                 
15422                 if ( cblack.indexOf(l) > -1) {
15423 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
15424                     //node.removeAttribute(n);
15425                     return true;
15426                 }
15427                 //Roo.log()
15428                 // only allow 'c whitelisted system attributes'
15429                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
15430 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
15431                     //node.removeAttribute(n);
15432                     return true;
15433                 }
15434                 
15435                 
15436                  
15437                 
15438                 clean.push(p);
15439                 return true;
15440             });
15441             if (clean.length) { 
15442                 node.setAttribute(n, clean.join(';'));
15443             } else {
15444                 node.removeAttribute(n);
15445             }
15446             
15447         }
15448         
15449         
15450         for (var i = node.attributes.length-1; i > -1 ; i--) {
15451             var a = node.attributes[i];
15452             //console.log(a);
15453             
15454             if (a.name.toLowerCase().substr(0,2)=='on')  {
15455                 node.removeAttribute(a.name);
15456                 continue;
15457             }
15458             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
15459                 node.removeAttribute(a.name);
15460                 continue;
15461             }
15462             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
15463                 cleanAttr(a.name,a.value); // fixme..
15464                 continue;
15465             }
15466             if (a.name == 'style') {
15467                 cleanStyle(a.name,a.value);
15468                 continue;
15469             }
15470             /// clean up MS crap..
15471             // tecnically this should be a list of valid class'es..
15472             
15473             
15474             if (a.name == 'class') {
15475                 if (a.value.match(/^Mso/)) {
15476                     node.className = '';
15477                 }
15478                 
15479                 if (a.value.match(/body/)) {
15480                     node.className = '';
15481                 }
15482                 continue;
15483             }
15484             
15485             // style cleanup!?
15486             // class cleanup?
15487             
15488         }
15489         
15490         
15491         this.cleanUpChildren(node);
15492         
15493         
15494     },
15495     /**
15496      * Clean up MS wordisms...
15497      */
15498     cleanWord : function(node)
15499     {
15500         var _t = this;
15501         var cleanWordChildren = function()
15502         {
15503             if (!node.childNodes.length) {
15504                 return;
15505             }
15506             for (var i = node.childNodes.length-1; i > -1 ; i--) {
15507                _t.cleanWord(node.childNodes[i]);
15508             }
15509         }
15510         
15511         
15512         if (!node) {
15513             this.cleanWord(this.doc.body);
15514             return;
15515         }
15516         if (node.nodeName == "#text") {
15517             // clean up silly Windows -- stuff?
15518             return; 
15519         }
15520         if (node.nodeName == "#comment") {
15521             node.parentNode.removeChild(node);
15522             // clean up silly Windows -- stuff?
15523             return; 
15524         }
15525         
15526         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
15527             node.parentNode.removeChild(node);
15528             return;
15529         }
15530         
15531         // remove - but keep children..
15532         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
15533             while (node.childNodes.length) {
15534                 var cn = node.childNodes[0];
15535                 node.removeChild(cn);
15536                 node.parentNode.insertBefore(cn, node);
15537             }
15538             node.parentNode.removeChild(node);
15539             cleanWordChildren();
15540             return;
15541         }
15542         // clean styles
15543         if (node.className.length) {
15544             
15545             var cn = node.className.split(/\W+/);
15546             var cna = [];
15547             Roo.each(cn, function(cls) {
15548                 if (cls.match(/Mso[a-zA-Z]+/)) {
15549                     return;
15550                 }
15551                 cna.push(cls);
15552             });
15553             node.className = cna.length ? cna.join(' ') : '';
15554             if (!cna.length) {
15555                 node.removeAttribute("class");
15556             }
15557         }
15558         
15559         if (node.hasAttribute("lang")) {
15560             node.removeAttribute("lang");
15561         }
15562         
15563         if (node.hasAttribute("style")) {
15564             
15565             var styles = node.getAttribute("style").split(";");
15566             var nstyle = [];
15567             Roo.each(styles, function(s) {
15568                 if (!s.match(/:/)) {
15569                     return;
15570                 }
15571                 var kv = s.split(":");
15572                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
15573                     return;
15574                 }
15575                 // what ever is left... we allow.
15576                 nstyle.push(s);
15577             });
15578             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
15579             if (!nstyle.length) {
15580                 node.removeAttribute('style');
15581             }
15582         }
15583         
15584         cleanWordChildren();
15585         
15586         
15587     },
15588     domToHTML : function(currentElement, depth, nopadtext) {
15589         
15590             depth = depth || 0;
15591             nopadtext = nopadtext || false;
15592         
15593             if (!currentElement) {
15594                 return this.domToHTML(this.doc.body);
15595             }
15596             
15597             //Roo.log(currentElement);
15598             var j;
15599             var allText = false;
15600             var nodeName = currentElement.nodeName;
15601             var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
15602             
15603             if  (nodeName == '#text') {
15604                 return currentElement.nodeValue;
15605             }
15606             
15607             
15608             var ret = '';
15609             if (nodeName != 'BODY') {
15610                  
15611                 var i = 0;
15612                 // Prints the node tagName, such as <A>, <IMG>, etc
15613                 if (tagName) {
15614                     var attr = [];
15615                     for(i = 0; i < currentElement.attributes.length;i++) {
15616                         // quoting?
15617                         var aname = currentElement.attributes.item(i).name;
15618                         if (!currentElement.attributes.item(i).value.length) {
15619                             continue;
15620                         }
15621                         attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
15622                     }
15623                     
15624                     ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
15625                 } 
15626                 else {
15627                     
15628                     // eack
15629                 }
15630             } else {
15631                 tagName = false;
15632             }
15633             if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
15634                 return ret;
15635             }
15636             if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
15637                 nopadtext = true;
15638             }
15639             
15640             
15641             // Traverse the tree
15642             i = 0;
15643             var currentElementChild = currentElement.childNodes.item(i);
15644             var allText = true;
15645             var innerHTML  = '';
15646             lastnode = '';
15647             while (currentElementChild) {
15648                 // Formatting code (indent the tree so it looks nice on the screen)
15649                 var nopad = nopadtext;
15650                 if (lastnode == 'SPAN') {
15651                     nopad  = true;
15652                 }
15653                 // text
15654                 if  (currentElementChild.nodeName == '#text') {
15655                     var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
15656                     if (!nopad && toadd.length > 80) {
15657                         innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
15658                     }
15659                     innerHTML  += toadd;
15660                     
15661                     i++;
15662                     currentElementChild = currentElement.childNodes.item(i);
15663                     lastNode = '';
15664                     continue;
15665                 }
15666                 allText = false;
15667                 
15668                 innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
15669                     
15670                 // Recursively traverse the tree structure of the child node
15671                 innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
15672                 lastnode = currentElementChild.nodeName;
15673                 i++;
15674                 currentElementChild=currentElement.childNodes.item(i);
15675             }
15676             
15677             ret += innerHTML;
15678             
15679             if (!allText) {
15680                     // The remaining code is mostly for formatting the tree
15681                 ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
15682             }
15683             
15684             
15685             if (tagName) {
15686                 ret+= "</"+tagName+">";
15687             }
15688             return ret;
15689             
15690         }
15691     
15692     // hide stuff that is not compatible
15693     /**
15694      * @event blur
15695      * @hide
15696      */
15697     /**
15698      * @event change
15699      * @hide
15700      */
15701     /**
15702      * @event focus
15703      * @hide
15704      */
15705     /**
15706      * @event specialkey
15707      * @hide
15708      */
15709     /**
15710      * @cfg {String} fieldClass @hide
15711      */
15712     /**
15713      * @cfg {String} focusClass @hide
15714      */
15715     /**
15716      * @cfg {String} autoCreate @hide
15717      */
15718     /**
15719      * @cfg {String} inputType @hide
15720      */
15721     /**
15722      * @cfg {String} invalidClass @hide
15723      */
15724     /**
15725      * @cfg {String} invalidText @hide
15726      */
15727     /**
15728      * @cfg {String} msgFx @hide
15729      */
15730     /**
15731      * @cfg {String} validateOnBlur @hide
15732      */
15733 });
15734
15735 Roo.HtmlEditorCore.white = [
15736         'area', 'br', 'img', 'input', 'hr', 'wbr',
15737         
15738        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
15739        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
15740        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
15741        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
15742        'table',   'ul',         'xmp', 
15743        
15744        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
15745       'thead',   'tr', 
15746      
15747       'dir', 'menu', 'ol', 'ul', 'dl',
15748        
15749       'embed',  'object'
15750 ];
15751
15752
15753 Roo.HtmlEditorCore.black = [
15754     //    'embed',  'object', // enable - backend responsiblity to clean thiese
15755         'applet', // 
15756         'base',   'basefont', 'bgsound', 'blink',  'body', 
15757         'frame',  'frameset', 'head',    'html',   'ilayer', 
15758         'iframe', 'layer',  'link',     'meta',    'object',   
15759         'script', 'style' ,'title',  'xml' // clean later..
15760 ];
15761 Roo.HtmlEditorCore.clean = [
15762     'script', 'style', 'title', 'xml'
15763 ];
15764 Roo.HtmlEditorCore.remove = [
15765     'font'
15766 ];
15767 // attributes..
15768
15769 Roo.HtmlEditorCore.ablack = [
15770     'on'
15771 ];
15772     
15773 Roo.HtmlEditorCore.aclean = [ 
15774     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
15775 ];
15776
15777 // protocols..
15778 Roo.HtmlEditorCore.pwhite= [
15779         'http',  'https',  'mailto'
15780 ];
15781
15782 // white listed style attributes.
15783 Roo.HtmlEditorCore.cwhite= [
15784       //  'text-align', /// default is to allow most things..
15785       
15786          
15787 //        'font-size'//??
15788 ];
15789
15790 // black listed style attributes.
15791 Roo.HtmlEditorCore.cblack= [
15792       //  'font-size' -- this can be set by the project 
15793 ];
15794
15795
15796 Roo.HtmlEditorCore.swapCodes   =[ 
15797     [    8211, "--" ], 
15798     [    8212, "--" ], 
15799     [    8216,  "'" ],  
15800     [    8217, "'" ],  
15801     [    8220, '"' ],  
15802     [    8221, '"' ],  
15803     [    8226, "*" ],  
15804     [    8230, "..." ]
15805 ]; 
15806
15807     /*
15808  * - LGPL
15809  *
15810  * HtmlEditor
15811  * 
15812  */
15813
15814 /**
15815  * @class Roo.bootstrap.HtmlEditor
15816  * @extends Roo.bootstrap.TextArea
15817  * Bootstrap HtmlEditor class
15818
15819  * @constructor
15820  * Create a new HtmlEditor
15821  * @param {Object} config The config object
15822  */
15823
15824 Roo.bootstrap.HtmlEditor = function(config){
15825     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
15826     if (!this.toolbars) {
15827         this.toolbars = [];
15828     }
15829     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
15830     this.addEvents({
15831             /**
15832              * @event initialize
15833              * Fires when the editor is fully initialized (including the iframe)
15834              * @param {HtmlEditor} this
15835              */
15836             initialize: true,
15837             /**
15838              * @event activate
15839              * Fires when the editor is first receives the focus. Any insertion must wait
15840              * until after this event.
15841              * @param {HtmlEditor} this
15842              */
15843             activate: true,
15844              /**
15845              * @event beforesync
15846              * Fires before the textarea is updated with content from the editor iframe. Return false
15847              * to cancel the sync.
15848              * @param {HtmlEditor} this
15849              * @param {String} html
15850              */
15851             beforesync: true,
15852              /**
15853              * @event beforepush
15854              * Fires before the iframe editor is updated with content from the textarea. Return false
15855              * to cancel the push.
15856              * @param {HtmlEditor} this
15857              * @param {String} html
15858              */
15859             beforepush: true,
15860              /**
15861              * @event sync
15862              * Fires when the textarea is updated with content from the editor iframe.
15863              * @param {HtmlEditor} this
15864              * @param {String} html
15865              */
15866             sync: true,
15867              /**
15868              * @event push
15869              * Fires when the iframe editor is updated with content from the textarea.
15870              * @param {HtmlEditor} this
15871              * @param {String} html
15872              */
15873             push: true,
15874              /**
15875              * @event editmodechange
15876              * Fires when the editor switches edit modes
15877              * @param {HtmlEditor} this
15878              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
15879              */
15880             editmodechange: true,
15881             /**
15882              * @event editorevent
15883              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
15884              * @param {HtmlEditor} this
15885              */
15886             editorevent: true,
15887             /**
15888              * @event firstfocus
15889              * Fires when on first focus - needed by toolbars..
15890              * @param {HtmlEditor} this
15891              */
15892             firstfocus: true,
15893             /**
15894              * @event autosave
15895              * Auto save the htmlEditor value as a file into Events
15896              * @param {HtmlEditor} this
15897              */
15898             autosave: true,
15899             /**
15900              * @event savedpreview
15901              * preview the saved version of htmlEditor
15902              * @param {HtmlEditor} this
15903              */
15904             savedpreview: true
15905         });
15906 };
15907
15908
15909 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
15910     
15911     
15912       /**
15913      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
15914      */
15915     toolbars : false,
15916    
15917      /**
15918      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
15919      *                        Roo.resizable.
15920      */
15921     resizable : false,
15922      /**
15923      * @cfg {Number} height (in pixels)
15924      */   
15925     height: 300,
15926    /**
15927      * @cfg {Number} width (in pixels)
15928      */   
15929     width: false,
15930     
15931     /**
15932      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
15933      * 
15934      */
15935     stylesheets: false,
15936     
15937     // id of frame..
15938     frameId: false,
15939     
15940     // private properties
15941     validationEvent : false,
15942     deferHeight: true,
15943     initialized : false,
15944     activated : false,
15945     
15946     onFocus : Roo.emptyFn,
15947     iframePad:3,
15948     hideMode:'offsets',
15949     
15950     
15951     tbContainer : false,
15952     
15953     toolbarContainer :function() {
15954         return this.wrap.select('.x-html-editor-tb',true).first();
15955     },
15956
15957     /**
15958      * Protected method that will not generally be called directly. It
15959      * is called when the editor creates its toolbar. Override this method if you need to
15960      * add custom toolbar buttons.
15961      * @param {HtmlEditor} editor
15962      */
15963     createToolbar : function(){
15964         
15965         Roo.log("create toolbars");
15966         
15967         this.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard({editor: this} ) ];
15968         this.toolbars[0].render(this.toolbarContainer());
15969         
15970         return;
15971         
15972 //        if (!editor.toolbars || !editor.toolbars.length) {
15973 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
15974 //        }
15975 //        
15976 //        for (var i =0 ; i < editor.toolbars.length;i++) {
15977 //            editor.toolbars[i] = Roo.factory(
15978 //                    typeof(editor.toolbars[i]) == 'string' ?
15979 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
15980 //                Roo.bootstrap.HtmlEditor);
15981 //            editor.toolbars[i].init(editor);
15982 //        }
15983     },
15984
15985      
15986     // private
15987     onRender : function(ct, position)
15988     {
15989        // Roo.log("Call onRender: " + this.xtype);
15990         var _t = this;
15991         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
15992       
15993         this.wrap = this.inputEl().wrap({
15994             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
15995         });
15996         
15997         this.editorcore.onRender(ct, position);
15998          
15999         if (this.resizable) {
16000             this.resizeEl = new Roo.Resizable(this.wrap, {
16001                 pinned : true,
16002                 wrap: true,
16003                 dynamic : true,
16004                 minHeight : this.height,
16005                 height: this.height,
16006                 handles : this.resizable,
16007                 width: this.width,
16008                 listeners : {
16009                     resize : function(r, w, h) {
16010                         _t.onResize(w,h); // -something
16011                     }
16012                 }
16013             });
16014             
16015         }
16016         this.createToolbar(this);
16017        
16018         
16019         if(!this.width && this.resizable){
16020             this.setSize(this.wrap.getSize());
16021         }
16022         if (this.resizeEl) {
16023             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
16024             // should trigger onReize..
16025         }
16026         
16027     },
16028
16029     // private
16030     onResize : function(w, h)
16031     {
16032         Roo.log('resize: ' +w + ',' + h );
16033         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
16034         var ew = false;
16035         var eh = false;
16036         
16037         if(this.inputEl() ){
16038             if(typeof w == 'number'){
16039                 var aw = w - this.wrap.getFrameWidth('lr');
16040                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
16041                 ew = aw;
16042             }
16043             if(typeof h == 'number'){
16044                  var tbh = -11;  // fixme it needs to tool bar size!
16045                 for (var i =0; i < this.toolbars.length;i++) {
16046                     // fixme - ask toolbars for heights?
16047                     tbh += this.toolbars[i].el.getHeight();
16048                     //if (this.toolbars[i].footer) {
16049                     //    tbh += this.toolbars[i].footer.el.getHeight();
16050                     //}
16051                 }
16052               
16053                 
16054                 
16055                 
16056                 
16057                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
16058                 ah -= 5; // knock a few pixes off for look..
16059                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
16060                 var eh = ah;
16061             }
16062         }
16063         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
16064         this.editorcore.onResize(ew,eh);
16065         
16066     },
16067
16068     /**
16069      * Toggles the editor between standard and source edit mode.
16070      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
16071      */
16072     toggleSourceEdit : function(sourceEditMode)
16073     {
16074         this.editorcore.toggleSourceEdit(sourceEditMode);
16075         
16076         if(this.editorcore.sourceEditMode){
16077             Roo.log('editor - showing textarea');
16078             
16079 //            Roo.log('in');
16080 //            Roo.log(this.syncValue());
16081             this.syncValue();
16082             this.inputEl().removeClass('hide');
16083             this.inputEl().dom.removeAttribute('tabIndex');
16084             this.inputEl().focus();
16085         }else{
16086             Roo.log('editor - hiding textarea');
16087 //            Roo.log('out')
16088 //            Roo.log(this.pushValue()); 
16089             this.pushValue();
16090             
16091             this.inputEl().addClass('hide');
16092             this.inputEl().dom.setAttribute('tabIndex', -1);
16093             //this.deferFocus();
16094         }
16095          
16096         if(this.resizable){
16097             this.setSize(this.wrap.getSize());
16098         }
16099         
16100         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
16101     },
16102  
16103     // private (for BoxComponent)
16104     adjustSize : Roo.BoxComponent.prototype.adjustSize,
16105
16106     // private (for BoxComponent)
16107     getResizeEl : function(){
16108         return this.wrap;
16109     },
16110
16111     // private (for BoxComponent)
16112     getPositionEl : function(){
16113         return this.wrap;
16114     },
16115
16116     // private
16117     initEvents : function(){
16118         this.originalValue = this.getValue();
16119     },
16120
16121 //    /**
16122 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
16123 //     * @method
16124 //     */
16125 //    markInvalid : Roo.emptyFn,
16126 //    /**
16127 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
16128 //     * @method
16129 //     */
16130 //    clearInvalid : Roo.emptyFn,
16131
16132     setValue : function(v){
16133         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
16134         this.editorcore.pushValue();
16135     },
16136
16137      
16138     // private
16139     deferFocus : function(){
16140         this.focus.defer(10, this);
16141     },
16142
16143     // doc'ed in Field
16144     focus : function(){
16145         this.editorcore.focus();
16146         
16147     },
16148       
16149
16150     // private
16151     onDestroy : function(){
16152         
16153         
16154         
16155         if(this.rendered){
16156             
16157             for (var i =0; i < this.toolbars.length;i++) {
16158                 // fixme - ask toolbars for heights?
16159                 this.toolbars[i].onDestroy();
16160             }
16161             
16162             this.wrap.dom.innerHTML = '';
16163             this.wrap.remove();
16164         }
16165     },
16166
16167     // private
16168     onFirstFocus : function(){
16169         //Roo.log("onFirstFocus");
16170         this.editorcore.onFirstFocus();
16171          for (var i =0; i < this.toolbars.length;i++) {
16172             this.toolbars[i].onFirstFocus();
16173         }
16174         
16175     },
16176     
16177     // private
16178     syncValue : function()
16179     {   
16180         this.editorcore.syncValue();
16181     },
16182     
16183     pushValue : function()
16184     {   
16185         this.editorcore.pushValue();
16186     }
16187      
16188     
16189     // hide stuff that is not compatible
16190     /**
16191      * @event blur
16192      * @hide
16193      */
16194     /**
16195      * @event change
16196      * @hide
16197      */
16198     /**
16199      * @event focus
16200      * @hide
16201      */
16202     /**
16203      * @event specialkey
16204      * @hide
16205      */
16206     /**
16207      * @cfg {String} fieldClass @hide
16208      */
16209     /**
16210      * @cfg {String} focusClass @hide
16211      */
16212     /**
16213      * @cfg {String} autoCreate @hide
16214      */
16215     /**
16216      * @cfg {String} inputType @hide
16217      */
16218     /**
16219      * @cfg {String} invalidClass @hide
16220      */
16221     /**
16222      * @cfg {String} invalidText @hide
16223      */
16224     /**
16225      * @cfg {String} msgFx @hide
16226      */
16227     /**
16228      * @cfg {String} validateOnBlur @hide
16229      */
16230 });
16231  
16232     
16233    
16234    
16235    
16236       
16237
16238 /**
16239  * @class Roo.bootstrap.HtmlEditorToolbar1
16240  * Basic Toolbar
16241  * 
16242  * Usage:
16243  *
16244  new Roo.bootstrap.HtmlEditor({
16245     ....
16246     toolbars : [
16247         new Roo.bootstrap.HtmlEditorToolbar1({
16248             disable : { fonts: 1 , format: 1, ..., ... , ...],
16249             btns : [ .... ]
16250         })
16251     }
16252      
16253  * 
16254  * @cfg {Object} disable List of elements to disable..
16255  * @cfg {Array} btns List of additional buttons.
16256  * 
16257  * 
16258  * NEEDS Extra CSS? 
16259  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
16260  */
16261  
16262 Roo.bootstrap.HtmlEditor.ToolbarStandard = function(config)
16263 {
16264     
16265     Roo.apply(this, config);
16266     
16267     // default disabled, based on 'good practice'..
16268     this.disable = this.disable || {};
16269     Roo.applyIf(this.disable, {
16270         fontSize : true,
16271         colors : true,
16272         specialElements : true
16273     });
16274     Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.constructor.call(this, config);
16275     
16276     this.editor = config.editor;
16277     this.editorcore = config.editor.editorcore;
16278     
16279     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
16280     
16281     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
16282     // dont call parent... till later.
16283 }
16284 Roo.extend(Roo.bootstrap.HtmlEditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
16285     
16286     
16287     bar : true,
16288     
16289     editor : false,
16290     editorcore : false,
16291     
16292     
16293     formats : [
16294         "p" ,  
16295         "h1","h2","h3","h4","h5","h6", 
16296         "pre", "code", 
16297         "abbr", "acronym", "address", "cite", "samp", "var",
16298         'div','span'
16299     ],
16300     
16301     onRender : function(ct, position)
16302     {
16303        // Roo.log("Call onRender: " + this.xtype);
16304         
16305        Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
16306        Roo.log(this.el);
16307        this.el.dom.style.marginBottom = '0';
16308        var _this = this;
16309        var editorcore = this.editorcore;
16310        var editor= this.editor;
16311        
16312        var children = [];
16313        var btn = function(id,cmd , toggle, handler){
16314        
16315             var  event = toggle ? 'toggle' : 'click';
16316        
16317             var a = {
16318                 size : 'sm',
16319                 xtype: 'Button',
16320                 xns: Roo.bootstrap,
16321                 glyphicon : id,
16322                 cmd : id || cmd,
16323                 enableToggle:toggle !== false,
16324                 //html : 'submit'
16325                 pressed : toggle ? false : null,
16326                 listeners : {}
16327             }
16328             a.listeners[toggle ? 'toggle' : 'click'] = function() {
16329                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
16330             }
16331             children.push(a);
16332             return a;
16333        }
16334         
16335         var style = {
16336                 xtype: 'Button',
16337                 size : 'sm',
16338                 xns: Roo.bootstrap,
16339                 glyphicon : 'font',
16340                 //html : 'submit'
16341                 menu : {
16342                     xtype: 'Menu',
16343                     xns: Roo.bootstrap,
16344                     items:  []
16345                 }
16346         };
16347         Roo.each(this.formats, function(f) {
16348             style.menu.items.push({
16349                 xtype :'MenuItem',
16350                 xns: Roo.bootstrap,
16351                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
16352                 tagname : f,
16353                 listeners : {
16354                     click : function()
16355                     {
16356                         editorcore.insertTag(this.tagname);
16357                         editor.focus();
16358                     }
16359                 }
16360                 
16361             });
16362         });
16363          children.push(style);   
16364             
16365             
16366         btn('bold',false,true);
16367         btn('italic',false,true);
16368         btn('align-left', 'justifyleft',true);
16369         btn('align-center', 'justifycenter',true);
16370         btn('align-right' , 'justifyright',true);
16371         btn('link', false, false, function(btn) {
16372             //Roo.log("create link?");
16373             var url = prompt(this.createLinkText, this.defaultLinkValue);
16374             if(url && url != 'http:/'+'/'){
16375                 this.editorcore.relayCmd('createlink', url);
16376             }
16377         }),
16378         btn('list','insertunorderedlist',true);
16379         btn('pencil', false,true, function(btn){
16380                 Roo.log(this);
16381                 
16382                 this.toggleSourceEdit(btn.pressed);
16383         });
16384         /*
16385         var cog = {
16386                 xtype: 'Button',
16387                 size : 'sm',
16388                 xns: Roo.bootstrap,
16389                 glyphicon : 'cog',
16390                 //html : 'submit'
16391                 menu : {
16392                     xtype: 'Menu',
16393                     xns: Roo.bootstrap,
16394                     items:  []
16395                 }
16396         };
16397         
16398         cog.menu.items.push({
16399             xtype :'MenuItem',
16400             xns: Roo.bootstrap,
16401             html : Clean styles,
16402             tagname : f,
16403             listeners : {
16404                 click : function()
16405                 {
16406                     editorcore.insertTag(this.tagname);
16407                     editor.focus();
16408                 }
16409             }
16410             
16411         });
16412        */
16413         
16414          
16415        this.xtype = 'NavSimplebar';
16416         
16417         for(var i=0;i< children.length;i++) {
16418             
16419             this.buttons.add(this.addxtypeChild(children[i]));
16420             
16421         }
16422         
16423         editor.on('editorevent', this.updateToolbar, this);
16424     },
16425     onBtnClick : function(id)
16426     {
16427        this.editorcore.relayCmd(id);
16428        this.editorcore.focus();
16429     },
16430     
16431     /**
16432      * Protected method that will not generally be called directly. It triggers
16433      * a toolbar update by reading the markup state of the current selection in the editor.
16434      */
16435     updateToolbar: function(){
16436
16437         if(!this.editorcore.activated){
16438             this.editor.onFirstFocus(); // is this neeed?
16439             return;
16440         }
16441
16442         var btns = this.buttons; 
16443         var doc = this.editorcore.doc;
16444         btns.get('bold').setActive(doc.queryCommandState('bold'));
16445         btns.get('italic').setActive(doc.queryCommandState('italic'));
16446         //btns.get('underline').setActive(doc.queryCommandState('underline'));
16447         
16448         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
16449         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
16450         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
16451         
16452         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
16453         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
16454          /*
16455         
16456         var ans = this.editorcore.getAllAncestors();
16457         if (this.formatCombo) {
16458             
16459             
16460             var store = this.formatCombo.store;
16461             this.formatCombo.setValue("");
16462             for (var i =0; i < ans.length;i++) {
16463                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
16464                     // select it..
16465                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
16466                     break;
16467                 }
16468             }
16469         }
16470         
16471         
16472         
16473         // hides menus... - so this cant be on a menu...
16474         Roo.bootstrap.MenuMgr.hideAll();
16475         */
16476         Roo.bootstrap.MenuMgr.hideAll();
16477         //this.editorsyncValue();
16478     },
16479     onFirstFocus: function() {
16480         this.buttons.each(function(item){
16481            item.enable();
16482         });
16483     },
16484     toggleSourceEdit : function(sourceEditMode){
16485         
16486           
16487         if(sourceEditMode){
16488             Roo.log("disabling buttons");
16489            this.buttons.each( function(item){
16490                 if(item.cmd != 'pencil'){
16491                     item.disable();
16492                 }
16493             });
16494           
16495         }else{
16496             Roo.log("enabling buttons");
16497             if(this.editorcore.initialized){
16498                 this.buttons.each( function(item){
16499                     item.enable();
16500                 });
16501             }
16502             
16503         }
16504         Roo.log("calling toggole on editor");
16505         // tell the editor that it's been pressed..
16506         this.editor.toggleSourceEdit(sourceEditMode);
16507        
16508     }
16509 });
16510
16511
16512
16513
16514
16515 /**
16516  * @class Roo.bootstrap.Table.AbstractSelectionModel
16517  * @extends Roo.util.Observable
16518  * Abstract base class for grid SelectionModels.  It provides the interface that should be
16519  * implemented by descendant classes.  This class should not be directly instantiated.
16520  * @constructor
16521  */
16522 Roo.bootstrap.Table.AbstractSelectionModel = function(){
16523     this.locked = false;
16524     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
16525 };
16526
16527
16528 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
16529     /** @ignore Called by the grid automatically. Do not call directly. */
16530     init : function(grid){
16531         this.grid = grid;
16532         this.initEvents();
16533     },
16534
16535     /**
16536      * Locks the selections.
16537      */
16538     lock : function(){
16539         this.locked = true;
16540     },
16541
16542     /**
16543      * Unlocks the selections.
16544      */
16545     unlock : function(){
16546         this.locked = false;
16547     },
16548
16549     /**
16550      * Returns true if the selections are locked.
16551      * @return {Boolean}
16552      */
16553     isLocked : function(){
16554         return this.locked;
16555     }
16556 });
16557 /**
16558  * @class Roo.bootstrap.Table.ColumnModel
16559  * @extends Roo.util.Observable
16560  * This is the default implementation of a ColumnModel used by the bootstrap table. It defines
16561  * the columns in the table.
16562  
16563  * @constructor
16564  * @param {Object} config An Array of column config objects. See this class's
16565  * config objects for details.
16566 */
16567 Roo.bootstrap.Table.ColumnModel = function(config){
16568         /**
16569      * The config passed into the constructor
16570      */
16571     this.config = config;
16572     this.lookup = {};
16573
16574     // if no id, create one
16575     // if the column does not have a dataIndex mapping,
16576     // map it to the order it is in the config
16577     for(var i = 0, len = config.length; i < len; i++){
16578         var c = config[i];
16579         if(typeof c.dataIndex == "undefined"){
16580             c.dataIndex = i;
16581         }
16582         if(typeof c.renderer == "string"){
16583             c.renderer = Roo.util.Format[c.renderer];
16584         }
16585         if(typeof c.id == "undefined"){
16586             c.id = Roo.id();
16587         }
16588 //        if(c.editor && c.editor.xtype){
16589 //            c.editor  = Roo.factory(c.editor, Roo.grid);
16590 //        }
16591 //        if(c.editor && c.editor.isFormField){
16592 //            c.editor = new Roo.grid.GridEditor(c.editor);
16593 //        }
16594
16595         this.lookup[c.id] = c;
16596     }
16597
16598     /**
16599      * The width of columns which have no width specified (defaults to 100)
16600      * @type Number
16601      */
16602     this.defaultWidth = 100;
16603
16604     /**
16605      * Default sortable of columns which have no sortable specified (defaults to false)
16606      * @type Boolean
16607      */
16608     this.defaultSortable = false;
16609
16610     this.addEvents({
16611         /**
16612              * @event widthchange
16613              * Fires when the width of a column changes.
16614              * @param {ColumnModel} this
16615              * @param {Number} columnIndex The column index
16616              * @param {Number} newWidth The new width
16617              */
16618             "widthchange": true,
16619         /**
16620              * @event headerchange
16621              * Fires when the text of a header changes.
16622              * @param {ColumnModel} this
16623              * @param {Number} columnIndex The column index
16624              * @param {Number} newText The new header text
16625              */
16626             "headerchange": true,
16627         /**
16628              * @event hiddenchange
16629              * Fires when a column is hidden or "unhidden".
16630              * @param {ColumnModel} this
16631              * @param {Number} columnIndex The column index
16632              * @param {Boolean} hidden true if hidden, false otherwise
16633              */
16634             "hiddenchange": true,
16635             /**
16636          * @event columnmoved
16637          * Fires when a column is moved.
16638          * @param {ColumnModel} this
16639          * @param {Number} oldIndex
16640          * @param {Number} newIndex
16641          */
16642         "columnmoved" : true,
16643         /**
16644          * @event columlockchange
16645          * Fires when a column's locked state is changed
16646          * @param {ColumnModel} this
16647          * @param {Number} colIndex
16648          * @param {Boolean} locked true if locked
16649          */
16650         "columnlockchange" : true
16651     });
16652     Roo.bootstrap.Table.ColumnModel.superclass.constructor.call(this);
16653 };
16654 Roo.extend(Roo.bootstrap.Table.ColumnModel, Roo.util.Observable, {
16655     /**
16656      * @cfg {String} header The header text to display in the Grid view.
16657      */
16658     /**
16659      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
16660      * {@link Roo.data.Record} definition from which to draw the column's value. If not
16661      * specified, the column's index is used as an index into the Record's data Array.
16662      */
16663     /**
16664      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
16665      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
16666      */
16667     /**
16668      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
16669      * Defaults to the value of the {@link #defaultSortable} property.
16670      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
16671      */
16672     /**
16673      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
16674      */
16675     /**
16676      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
16677      */
16678     /**
16679      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
16680      */
16681     /**
16682      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
16683      */
16684     /**
16685      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
16686      * given the cell's data value. See {@link #setRenderer}. If not specified, the
16687      * default renderer uses the raw data value.
16688      */
16689     /**
16690      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
16691      */
16692
16693     /**
16694      * Returns the id of the column at the specified index.
16695      * @param {Number} index The column index
16696      * @return {String} the id
16697      */
16698     getColumnId : function(index){
16699         return this.config[index].id;
16700     },
16701
16702     /**
16703      * Returns the column for a specified id.
16704      * @param {String} id The column id
16705      * @return {Object} the column
16706      */
16707     getColumnById : function(id){
16708         return this.lookup[id];
16709     },
16710
16711     
16712     /**
16713      * Returns the column for a specified dataIndex.
16714      * @param {String} dataIndex The column dataIndex
16715      * @return {Object|Boolean} the column or false if not found
16716      */
16717     getColumnByDataIndex: function(dataIndex){
16718         var index = this.findColumnIndex(dataIndex);
16719         return index > -1 ? this.config[index] : false;
16720     },
16721     
16722     /**
16723      * Returns the index for a specified column id.
16724      * @param {String} id The column id
16725      * @return {Number} the index, or -1 if not found
16726      */
16727     getIndexById : function(id){
16728         for(var i = 0, len = this.config.length; i < len; i++){
16729             if(this.config[i].id == id){
16730                 return i;
16731             }
16732         }
16733         return -1;
16734     },
16735     
16736     /**
16737      * Returns the index for a specified column dataIndex.
16738      * @param {String} dataIndex The column dataIndex
16739      * @return {Number} the index, or -1 if not found
16740      */
16741     
16742     findColumnIndex : function(dataIndex){
16743         for(var i = 0, len = this.config.length; i < len; i++){
16744             if(this.config[i].dataIndex == dataIndex){
16745                 return i;
16746             }
16747         }
16748         return -1;
16749     },
16750     
16751     
16752     moveColumn : function(oldIndex, newIndex){
16753         var c = this.config[oldIndex];
16754         this.config.splice(oldIndex, 1);
16755         this.config.splice(newIndex, 0, c);
16756         this.dataMap = null;
16757         this.fireEvent("columnmoved", this, oldIndex, newIndex);
16758     },
16759
16760     isLocked : function(colIndex){
16761         return this.config[colIndex].locked === true;
16762     },
16763
16764     setLocked : function(colIndex, value, suppressEvent){
16765         if(this.isLocked(colIndex) == value){
16766             return;
16767         }
16768         this.config[colIndex].locked = value;
16769         if(!suppressEvent){
16770             this.fireEvent("columnlockchange", this, colIndex, value);
16771         }
16772     },
16773
16774     getTotalLockedWidth : function(){
16775         var totalWidth = 0;
16776         for(var i = 0; i < this.config.length; i++){
16777             if(this.isLocked(i) && !this.isHidden(i)){
16778                 this.totalWidth += this.getColumnWidth(i);
16779             }
16780         }
16781         return totalWidth;
16782     },
16783
16784     getLockedCount : function(){
16785         for(var i = 0, len = this.config.length; i < len; i++){
16786             if(!this.isLocked(i)){
16787                 return i;
16788             }
16789         }
16790     },
16791
16792     /**
16793      * Returns the number of columns.
16794      * @return {Number}
16795      */
16796     getColumnCount : function(visibleOnly){
16797         if(visibleOnly === true){
16798             var c = 0;
16799             for(var i = 0, len = this.config.length; i < len; i++){
16800                 if(!this.isHidden(i)){
16801                     c++;
16802                 }
16803             }
16804             return c;
16805         }
16806         return this.config.length;
16807     },
16808
16809     /**
16810      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
16811      * @param {Function} fn
16812      * @param {Object} scope (optional)
16813      * @return {Array} result
16814      */
16815     getColumnsBy : function(fn, scope){
16816         var r = [];
16817         for(var i = 0, len = this.config.length; i < len; i++){
16818             var c = this.config[i];
16819             if(fn.call(scope||this, c, i) === true){
16820                 r[r.length] = c;
16821             }
16822         }
16823         return r;
16824     },
16825
16826     /**
16827      * Returns true if the specified column is sortable.
16828      * @param {Number} col The column index
16829      * @return {Boolean}
16830      */
16831     isSortable : function(col){
16832         if(typeof this.config[col].sortable == "undefined"){
16833             return this.defaultSortable;
16834         }
16835         return this.config[col].sortable;
16836     },
16837
16838     /**
16839      * Returns the rendering (formatting) function defined for the column.
16840      * @param {Number} col The column index.
16841      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
16842      */
16843     getRenderer : function(col){
16844         if(!this.config[col].renderer){
16845             return Roo.bootstrap.Table.ColumnModel.defaultRenderer;
16846         }
16847         return this.config[col].renderer;
16848     },
16849
16850     /**
16851      * Sets the rendering (formatting) function for a column.
16852      * @param {Number} col The column index
16853      * @param {Function} fn The function to use to process the cell's raw data
16854      * to return HTML markup for the grid view. The render function is called with
16855      * the following parameters:<ul>
16856      * <li>Data value.</li>
16857      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
16858      * <li>css A CSS style string to apply to the table cell.</li>
16859      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
16860      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
16861      * <li>Row index</li>
16862      * <li>Column index</li>
16863      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
16864      */
16865     setRenderer : function(col, fn){
16866         this.config[col].renderer = fn;
16867     },
16868
16869     /**
16870      * Returns the width for the specified column.
16871      * @param {Number} col The column index
16872      * @return {Number}
16873      */
16874     getColumnWidth : function(col){
16875         return this.config[col].width * 1 || this.defaultWidth;
16876     },
16877
16878     /**
16879      * Sets the width for a column.
16880      * @param {Number} col The column index
16881      * @param {Number} width The new width
16882      */
16883     setColumnWidth : function(col, width, suppressEvent){
16884         this.config[col].width = width;
16885         this.totalWidth = null;
16886         if(!suppressEvent){
16887              this.fireEvent("widthchange", this, col, width);
16888         }
16889     },
16890
16891     /**
16892      * Returns the total width of all columns.
16893      * @param {Boolean} includeHidden True to include hidden column widths
16894      * @return {Number}
16895      */
16896     getTotalWidth : function(includeHidden){
16897         if(!this.totalWidth){
16898             this.totalWidth = 0;
16899             for(var i = 0, len = this.config.length; i < len; i++){
16900                 if(includeHidden || !this.isHidden(i)){
16901                     this.totalWidth += this.getColumnWidth(i);
16902                 }
16903             }
16904         }
16905         return this.totalWidth;
16906     },
16907
16908     /**
16909      * Returns the header for the specified column.
16910      * @param {Number} col The column index
16911      * @return {String}
16912      */
16913     getColumnHeader : function(col){
16914         return this.config[col].header;
16915     },
16916
16917     /**
16918      * Sets the header for a column.
16919      * @param {Number} col The column index
16920      * @param {String} header The new header
16921      */
16922     setColumnHeader : function(col, header){
16923         this.config[col].header = header;
16924         this.fireEvent("headerchange", this, col, header);
16925     },
16926
16927     /**
16928      * Returns the tooltip for the specified column.
16929      * @param {Number} col The column index
16930      * @return {String}
16931      */
16932     getColumnTooltip : function(col){
16933             return this.config[col].tooltip;
16934     },
16935     /**
16936      * Sets the tooltip for a column.
16937      * @param {Number} col The column index
16938      * @param {String} tooltip The new tooltip
16939      */
16940     setColumnTooltip : function(col, tooltip){
16941             this.config[col].tooltip = tooltip;
16942     },
16943
16944     /**
16945      * Returns the dataIndex for the specified column.
16946      * @param {Number} col The column index
16947      * @return {Number}
16948      */
16949     getDataIndex : function(col){
16950         return this.config[col].dataIndex;
16951     },
16952
16953     /**
16954      * Sets the dataIndex for a column.
16955      * @param {Number} col The column index
16956      * @param {Number} dataIndex The new dataIndex
16957      */
16958     setDataIndex : function(col, dataIndex){
16959         this.config[col].dataIndex = dataIndex;
16960     },
16961
16962     
16963     
16964     /**
16965      * Returns true if the cell is editable.
16966      * @param {Number} colIndex The column index
16967      * @param {Number} rowIndex The row index
16968      * @return {Boolean}
16969      */
16970     isCellEditable : function(colIndex, rowIndex){
16971         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
16972     },
16973
16974     /**
16975      * Returns the editor defined for the cell/column.
16976      * return false or null to disable editing.
16977      * @param {Number} colIndex The column index
16978      * @param {Number} rowIndex The row index
16979      * @return {Object}
16980      */
16981     getCellEditor : function(colIndex, rowIndex){
16982         return this.config[colIndex].editor;
16983     },
16984
16985     /**
16986      * Sets if a column is editable.
16987      * @param {Number} col The column index
16988      * @param {Boolean} editable True if the column is editable
16989      */
16990     setEditable : function(col, editable){
16991         this.config[col].editable = editable;
16992     },
16993
16994
16995     /**
16996      * Returns true if the column is hidden.
16997      * @param {Number} colIndex The column index
16998      * @return {Boolean}
16999      */
17000     isHidden : function(colIndex){
17001         return this.config[colIndex].hidden;
17002     },
17003
17004
17005     /**
17006      * Returns true if the column width cannot be changed
17007      */
17008     isFixed : function(colIndex){
17009         return this.config[colIndex].fixed;
17010     },
17011
17012     /**
17013      * Returns true if the column can be resized
17014      * @return {Boolean}
17015      */
17016     isResizable : function(colIndex){
17017         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
17018     },
17019     /**
17020      * Sets if a column is hidden.
17021      * @param {Number} colIndex The column index
17022      * @param {Boolean} hidden True if the column is hidden
17023      */
17024     setHidden : function(colIndex, hidden){
17025         this.config[colIndex].hidden = hidden;
17026         this.totalWidth = null;
17027         this.fireEvent("hiddenchange", this, colIndex, hidden);
17028     },
17029
17030     /**
17031      * Sets the editor for a column.
17032      * @param {Number} col The column index
17033      * @param {Object} editor The editor object
17034      */
17035     setEditor : function(col, editor){
17036         this.config[col].editor = editor;
17037     }
17038 });
17039
17040 Roo.bootstrap.Table.ColumnModel.defaultRenderer = function(value){
17041         if(typeof value == "string" && value.length < 1){
17042             return "&#160;";
17043         }
17044         return value;
17045 };
17046
17047 // Alias for backwards compatibility
17048 Roo.bootstrap.Table.DefaultColumnModel = Roo.bootstrap.Table.ColumnModel;
17049
17050 /**
17051  * @extends Roo.bootstrap.Table.AbstractSelectionModel
17052  * @class Roo.bootstrap.Table.RowSelectionModel
17053  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
17054  * It supports multiple selections and keyboard selection/navigation. 
17055  * @constructor
17056  * @param {Object} config
17057  */
17058
17059 Roo.bootstrap.Table.RowSelectionModel = function(config){
17060     Roo.apply(this, config);
17061     this.selections = new Roo.util.MixedCollection(false, function(o){
17062         return o.id;
17063     });
17064
17065     this.last = false;
17066     this.lastActive = false;
17067
17068     this.addEvents({
17069         /**
17070              * @event selectionchange
17071              * Fires when the selection changes
17072              * @param {SelectionModel} this
17073              */
17074             "selectionchange" : true,
17075         /**
17076              * @event afterselectionchange
17077              * Fires after the selection changes (eg. by key press or clicking)
17078              * @param {SelectionModel} this
17079              */
17080             "afterselectionchange" : true,
17081         /**
17082              * @event beforerowselect
17083              * Fires when a row is selected being selected, return false to cancel.
17084              * @param {SelectionModel} this
17085              * @param {Number} rowIndex The selected index
17086              * @param {Boolean} keepExisting False if other selections will be cleared
17087              */
17088             "beforerowselect" : true,
17089         /**
17090              * @event rowselect
17091              * Fires when a row is selected.
17092              * @param {SelectionModel} this
17093              * @param {Number} rowIndex The selected index
17094              * @param {Roo.data.Record} r The record
17095              */
17096             "rowselect" : true,
17097         /**
17098              * @event rowdeselect
17099              * Fires when a row is deselected.
17100              * @param {SelectionModel} this
17101              * @param {Number} rowIndex The selected index
17102              */
17103         "rowdeselect" : true
17104     });
17105     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
17106     this.locked = false;
17107 };
17108
17109 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
17110     /**
17111      * @cfg {Boolean} singleSelect
17112      * True to allow selection of only one row at a time (defaults to false)
17113      */
17114     singleSelect : false,
17115
17116     // private
17117     initEvents : function(){
17118
17119         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
17120             this.grid.on("mousedown", this.handleMouseDown, this);
17121         }else{ // allow click to work like normal
17122             this.grid.on("rowclick", this.handleDragableRowClick, this);
17123         }
17124
17125         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
17126             "up" : function(e){
17127                 if(!e.shiftKey){
17128                     this.selectPrevious(e.shiftKey);
17129                 }else if(this.last !== false && this.lastActive !== false){
17130                     var last = this.last;
17131                     this.selectRange(this.last,  this.lastActive-1);
17132                     this.grid.getView().focusRow(this.lastActive);
17133                     if(last !== false){
17134                         this.last = last;
17135                     }
17136                 }else{
17137                     this.selectFirstRow();
17138                 }
17139                 this.fireEvent("afterselectionchange", this);
17140             },
17141             "down" : function(e){
17142                 if(!e.shiftKey){
17143                     this.selectNext(e.shiftKey);
17144                 }else if(this.last !== false && this.lastActive !== false){
17145                     var last = this.last;
17146                     this.selectRange(this.last,  this.lastActive+1);
17147                     this.grid.getView().focusRow(this.lastActive);
17148                     if(last !== false){
17149                         this.last = last;
17150                     }
17151                 }else{
17152                     this.selectFirstRow();
17153                 }
17154                 this.fireEvent("afterselectionchange", this);
17155             },
17156             scope: this
17157         });
17158
17159         var view = this.grid.view;
17160         view.on("refresh", this.onRefresh, this);
17161         view.on("rowupdated", this.onRowUpdated, this);
17162         view.on("rowremoved", this.onRemove, this);
17163     },
17164
17165     // private
17166     onRefresh : function(){
17167         var ds = this.grid.dataSource, i, v = this.grid.view;
17168         var s = this.selections;
17169         s.each(function(r){
17170             if((i = ds.indexOfId(r.id)) != -1){
17171                 v.onRowSelect(i);
17172             }else{
17173                 s.remove(r);
17174             }
17175         });
17176     },
17177
17178     // private
17179     onRemove : function(v, index, r){
17180         this.selections.remove(r);
17181     },
17182
17183     // private
17184     onRowUpdated : function(v, index, r){
17185         if(this.isSelected(r)){
17186             v.onRowSelect(index);
17187         }
17188     },
17189
17190     /**
17191      * Select records.
17192      * @param {Array} records The records to select
17193      * @param {Boolean} keepExisting (optional) True to keep existing selections
17194      */
17195     selectRecords : function(records, keepExisting){
17196         if(!keepExisting){
17197             this.clearSelections();
17198         }
17199         var ds = this.grid.dataSource;
17200         for(var i = 0, len = records.length; i < len; i++){
17201             this.selectRow(ds.indexOf(records[i]), true);
17202         }
17203     },
17204
17205     /**
17206      * Gets the number of selected rows.
17207      * @return {Number}
17208      */
17209     getCount : function(){
17210         return this.selections.length;
17211     },
17212
17213     /**
17214      * Selects the first row in the grid.
17215      */
17216     selectFirstRow : function(){
17217         this.selectRow(0);
17218     },
17219
17220     /**
17221      * Select the last row.
17222      * @param {Boolean} keepExisting (optional) True to keep existing selections
17223      */
17224     selectLastRow : function(keepExisting){
17225         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
17226     },
17227
17228     /**
17229      * Selects the row immediately following the last selected row.
17230      * @param {Boolean} keepExisting (optional) True to keep existing selections
17231      */
17232     selectNext : function(keepExisting){
17233         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
17234             this.selectRow(this.last+1, keepExisting);
17235             this.grid.getView().focusRow(this.last);
17236         }
17237     },
17238
17239     /**
17240      * Selects the row that precedes the last selected row.
17241      * @param {Boolean} keepExisting (optional) True to keep existing selections
17242      */
17243     selectPrevious : function(keepExisting){
17244         if(this.last){
17245             this.selectRow(this.last-1, keepExisting);
17246             this.grid.getView().focusRow(this.last);
17247         }
17248     },
17249
17250     /**
17251      * Returns the selected records
17252      * @return {Array} Array of selected records
17253      */
17254     getSelections : function(){
17255         return [].concat(this.selections.items);
17256     },
17257
17258     /**
17259      * Returns the first selected record.
17260      * @return {Record}
17261      */
17262     getSelected : function(){
17263         return this.selections.itemAt(0);
17264     },
17265
17266
17267     /**
17268      * Clears all selections.
17269      */
17270     clearSelections : function(fast){
17271         if(this.locked) return;
17272         if(fast !== true){
17273             var ds = this.grid.dataSource;
17274             var s = this.selections;
17275             s.each(function(r){
17276                 this.deselectRow(ds.indexOfId(r.id));
17277             }, this);
17278             s.clear();
17279         }else{
17280             this.selections.clear();
17281         }
17282         this.last = false;
17283     },
17284
17285
17286     /**
17287      * Selects all rows.
17288      */
17289     selectAll : function(){
17290         if(this.locked) return;
17291         this.selections.clear();
17292         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
17293             this.selectRow(i, true);
17294         }
17295     },
17296
17297     /**
17298      * Returns True if there is a selection.
17299      * @return {Boolean}
17300      */
17301     hasSelection : function(){
17302         return this.selections.length > 0;
17303     },
17304
17305     /**
17306      * Returns True if the specified row is selected.
17307      * @param {Number/Record} record The record or index of the record to check
17308      * @return {Boolean}
17309      */
17310     isSelected : function(index){
17311         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
17312         return (r && this.selections.key(r.id) ? true : false);
17313     },
17314
17315     /**
17316      * Returns True if the specified record id is selected.
17317      * @param {String} id The id of record to check
17318      * @return {Boolean}
17319      */
17320     isIdSelected : function(id){
17321         return (this.selections.key(id) ? true : false);
17322     },
17323
17324     // private
17325     handleMouseDown : function(e, t){
17326         var view = this.grid.getView(), rowIndex;
17327         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
17328             return;
17329         };
17330         if(e.shiftKey && this.last !== false){
17331             var last = this.last;
17332             this.selectRange(last, rowIndex, e.ctrlKey);
17333             this.last = last; // reset the last
17334             view.focusRow(rowIndex);
17335         }else{
17336             var isSelected = this.isSelected(rowIndex);
17337             if(e.button !== 0 && isSelected){
17338                 view.focusRow(rowIndex);
17339             }else if(e.ctrlKey && isSelected){
17340                 this.deselectRow(rowIndex);
17341             }else if(!isSelected){
17342                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
17343                 view.focusRow(rowIndex);
17344             }
17345         }
17346         this.fireEvent("afterselectionchange", this);
17347     },
17348     // private
17349     handleDragableRowClick :  function(grid, rowIndex, e) 
17350     {
17351         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
17352             this.selectRow(rowIndex, false);
17353             grid.view.focusRow(rowIndex);
17354              this.fireEvent("afterselectionchange", this);
17355         }
17356     },
17357     
17358     /**
17359      * Selects multiple rows.
17360      * @param {Array} rows Array of the indexes of the row to select
17361      * @param {Boolean} keepExisting (optional) True to keep existing selections
17362      */
17363     selectRows : function(rows, keepExisting){
17364         if(!keepExisting){
17365             this.clearSelections();
17366         }
17367         for(var i = 0, len = rows.length; i < len; i++){
17368             this.selectRow(rows[i], true);
17369         }
17370     },
17371
17372     /**
17373      * Selects a range of rows. All rows in between startRow and endRow are also selected.
17374      * @param {Number} startRow The index of the first row in the range
17375      * @param {Number} endRow The index of the last row in the range
17376      * @param {Boolean} keepExisting (optional) True to retain existing selections
17377      */
17378     selectRange : function(startRow, endRow, keepExisting){
17379         if(this.locked) return;
17380         if(!keepExisting){
17381             this.clearSelections();
17382         }
17383         if(startRow <= endRow){
17384             for(var i = startRow; i <= endRow; i++){
17385                 this.selectRow(i, true);
17386             }
17387         }else{
17388             for(var i = startRow; i >= endRow; i--){
17389                 this.selectRow(i, true);
17390             }
17391         }
17392     },
17393
17394     /**
17395      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
17396      * @param {Number} startRow The index of the first row in the range
17397      * @param {Number} endRow The index of the last row in the range
17398      */
17399     deselectRange : function(startRow, endRow, preventViewNotify){
17400         if(this.locked) return;
17401         for(var i = startRow; i <= endRow; i++){
17402             this.deselectRow(i, preventViewNotify);
17403         }
17404     },
17405
17406     /**
17407      * Selects a row.
17408      * @param {Number} row The index of the row to select
17409      * @param {Boolean} keepExisting (optional) True to keep existing selections
17410      */
17411     selectRow : function(index, keepExisting, preventViewNotify){
17412         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
17413         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
17414             if(!keepExisting || this.singleSelect){
17415                 this.clearSelections();
17416             }
17417             var r = this.grid.dataSource.getAt(index);
17418             this.selections.add(r);
17419             this.last = this.lastActive = index;
17420             if(!preventViewNotify){
17421                 this.grid.getView().onRowSelect(index);
17422             }
17423             this.fireEvent("rowselect", this, index, r);
17424             this.fireEvent("selectionchange", this);
17425         }
17426     },
17427
17428     /**
17429      * Deselects a row.
17430      * @param {Number} row The index of the row to deselect
17431      */
17432     deselectRow : function(index, preventViewNotify){
17433         if(this.locked) return;
17434         if(this.last == index){
17435             this.last = false;
17436         }
17437         if(this.lastActive == index){
17438             this.lastActive = false;
17439         }
17440         var r = this.grid.dataSource.getAt(index);
17441         this.selections.remove(r);
17442         if(!preventViewNotify){
17443             this.grid.getView().onRowDeselect(index);
17444         }
17445         this.fireEvent("rowdeselect", this, index);
17446         this.fireEvent("selectionchange", this);
17447     },
17448
17449     // private
17450     restoreLast : function(){
17451         if(this._last){
17452             this.last = this._last;
17453         }
17454     },
17455
17456     // private
17457     acceptsNav : function(row, col, cm){
17458         return !cm.isHidden(col) && cm.isCellEditable(col, row);
17459     },
17460
17461     // private
17462     onEditorKey : function(field, e){
17463         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
17464         if(k == e.TAB){
17465             e.stopEvent();
17466             ed.completeEdit();
17467             if(e.shiftKey){
17468                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
17469             }else{
17470                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
17471             }
17472         }else if(k == e.ENTER && !e.ctrlKey){
17473             e.stopEvent();
17474             ed.completeEdit();
17475             if(e.shiftKey){
17476                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
17477             }else{
17478                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
17479             }
17480         }else if(k == e.ESC){
17481             ed.cancelEdit();
17482         }
17483         if(newCell){
17484             g.startEditing(newCell[0], newCell[1]);
17485         }
17486     }
17487 });/*
17488  * - LGPL
17489  *
17490  * element
17491  * 
17492  */
17493
17494 /**
17495  * @class Roo.bootstrap.MessageBar
17496  * @extends Roo.bootstrap.Component
17497  * Bootstrap MessageBar class
17498  * @cfg {String} html contents of the MessageBar
17499  * @cfg {String} weight (info | success | warning | danger) default info
17500  * @cfg {String} beforeClass insert the bar before the given class
17501  * @cfg {Boolean} closable (true | false) default false
17502  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
17503  * 
17504  * @constructor
17505  * Create a new Element
17506  * @param {Object} config The config object
17507  */
17508
17509 Roo.bootstrap.MessageBar = function(config){
17510     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
17511 };
17512
17513 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
17514     
17515     html: '',
17516     weight: 'info',
17517     closable: false,
17518     fixed: false,
17519     beforeClass: 'bootstrap-sticky-wrap',
17520     
17521     getAutoCreate : function(){
17522         
17523         var cfg = {
17524             tag: 'div',
17525             cls: 'alert alert-dismissable alert-' + this.weight,
17526             cn: [
17527                 {
17528                     tag: 'span',
17529                     cls: 'message',
17530                     html: this.html || ''
17531                 }
17532             ]
17533         }
17534         
17535         if(this.fixed){
17536             cfg.cls += ' alert-messages-fixed';
17537         }
17538         
17539         if(this.closable){
17540             cfg.cn.push({
17541                 tag: 'button',
17542                 cls: 'close',
17543                 html: 'x'
17544             });
17545         }
17546         
17547         return cfg;
17548     },
17549     
17550     onRender : function(ct, position)
17551     {
17552         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17553         
17554         if(!this.el){
17555             var cfg = Roo.apply({},  this.getAutoCreate());
17556             cfg.id = Roo.id();
17557             
17558             if (this.cls) {
17559                 cfg.cls += ' ' + this.cls;
17560             }
17561             if (this.style) {
17562                 cfg.style = this.style;
17563             }
17564             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
17565             
17566             this.el.setVisibilityMode(Roo.Element.DISPLAY);
17567         }
17568         
17569         this.el.select('>button.close').on('click', this.hide, this);
17570         
17571     },
17572     
17573     show : function()
17574     {
17575         if (!this.rendered) {
17576             this.render();
17577         }
17578         
17579         this.el.show();
17580         
17581         this.fireEvent('show', this);
17582         
17583     },
17584     
17585     hide : function()
17586     {
17587         if (!this.rendered) {
17588             this.render();
17589         }
17590         
17591         this.el.hide();
17592         
17593         this.fireEvent('hide', this);
17594     },
17595     
17596     update : function()
17597     {
17598 //        var e = this.el.dom.firstChild;
17599 //        
17600 //        if(this.closable){
17601 //            e = e.nextSibling;
17602 //        }
17603 //        
17604 //        e.data = this.html || '';
17605
17606         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
17607     }
17608    
17609 });
17610
17611  
17612
17613