buildSDK/dependancy_bootstrap.txt
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * 
20  * @constructor
21  * Do not use directly - it does not do anything..
22  * @param {Object} config The config object
23  */
24
25
26
27 Roo.bootstrap.Component = function(config){
28     Roo.bootstrap.Component.superclass.constructor.call(this, config);
29 };
30
31 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
32     
33     
34     allowDomMove : false, // to stop relocations in parent onRender...
35     
36     cls : false,
37     
38     style : false,
39     
40     autoCreate : false,
41     
42     initEvents : function() {  },
43     
44     xattr : false,
45     
46     parentId : false,
47     
48     can_build_overlaid : true,
49     
50     dataId : false,
51     
52     name : false,
53     
54     parent: function() {
55         // returns the parent component..
56         return Roo.ComponentMgr.get(this.parentId)
57         
58         
59     },
60     
61     // private
62     onRender : function(ct, position)
63     {
64        // Roo.log("Call onRender: " + this.xtype);
65         
66         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
67         
68         if(this.el){
69             if (this.el.attr('xtype')) {
70                 this.el.attr('xtypex', this.el.attr('xtype'));
71                 this.el.dom.removeAttribute('xtype');
72                 
73                 this.initEvents();
74             }
75             
76             return;
77         }
78         
79          
80         
81         var cfg = Roo.apply({},  this.getAutoCreate());
82         cfg.id = Roo.id();
83         
84         // fill in the extra attributes 
85         if (this.xattr && typeof(this.xattr) =='object') {
86             for (var i in this.xattr) {
87                 cfg[i] = this.xattr[i];
88             }
89         }
90         
91         if(this.dataId){
92             cfg.dataId = this.dataId;
93         }
94         
95         if (this.cls) {
96             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
97         }
98         
99         if (this.style) { // fixme needs to support more complex style data.
100             cfg.style = this.style;
101         }
102         
103         if(this.name){
104             cfg.name = this.name;
105         }
106         
107         this.el = ct.createChild(cfg, position);
108         
109         if(this.tabIndex !== undefined){
110             this.el.dom.setAttribute('tabIndex', this.tabIndex);
111         }
112         this.initEvents();
113         
114         
115     },
116     
117     getChildContainer : function()
118     {
119         return this.el;
120     },
121     
122     
123     addxtype  : function(tree,cntr)
124     {
125         var cn = this;
126         
127         cn = Roo.factory(tree);
128            
129         cn.parentType = this.xtype; //??
130         cn.parentId = this.id;
131         
132         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
133         
134         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
135         
136         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
137         
138         var build_from_html =  Roo.XComponent.build_from_html;
139           
140         var is_body  = (tree.xtype == 'Body') ;
141           
142         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
143           
144         var self_cntr_el = Roo.get(this[cntr](false));
145         
146         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
147             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
148                 return this.addxtypeChild(tree,cntr);
149             }
150             
151             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
152                 
153             if(echild){
154                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
155             }
156             
157             Roo.log('skipping render');
158             return cn;
159             
160         }
161         
162         var ret = false;
163         
164         while (true) {
165             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
166             
167             if (!echild) {
168                 break;
169             }
170             
171             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
172                 break;
173             }
174             
175             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
176         }
177         return ret;
178     },
179     
180     addxtypeChild : function (tree, cntr)
181     {
182         Roo.log('addxtypeChild:' + cntr);
183         var cn = this;
184         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
185         
186         
187         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
188                     (typeof(tree['flexy:foreach']) != 'undefined');
189           
190         
191         
192         
193         // render the element if it's not BODY.
194         if (tree.xtype != 'Body') {
195            
196             cn = Roo.factory(tree);
197            
198             cn.parentType = this.xtype; //??
199             cn.parentId = this.id;
200             
201             var build_from_html =  Roo.XComponent.build_from_html;
202             
203             
204             // does the container contain child eleemnts with 'xtype' attributes.
205             // that match this xtype..
206             // note - when we render we create these as well..
207             // so we should check to see if body has xtype set.
208             if (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
209                
210                 var self_cntr_el = Roo.get(this[cntr](false));
211                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
212                 
213                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
214                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
215                   
216                   
217                   
218                     cn.el = echild;
219                   //  Roo.log("GOT");
220                     //echild.dom.removeAttribute('xtype');
221                 } else {
222                     Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
223                    
224                 }
225             }
226            
227             
228                
229             // if object has flexy:if - then it may or may not be rendered.
230             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
231                 // skip a flexy if element.
232                 Roo.log('skipping render');
233              } else {
234                  
235                 // actually if flexy:foreach is found, we really want to create 
236                 // multiple copies here...
237                 //Roo.log('render');
238                 //Roo.log(this[cntr]());
239                 cn.render(this[cntr](true));
240              }
241             // then add the element..
242         }
243         
244         
245         // handle the kids..
246         
247         var nitems = [];
248         /*
249         if (typeof (tree.menu) != 'undefined') {
250             tree.menu.parentType = cn.xtype;
251             tree.menu.triggerEl = cn.el;
252             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
253             
254         }
255         */
256         if (!tree.items || !tree.items.length) {
257             cn.items = nitems;
258             return cn;
259         }
260         var items = tree.items;
261         delete tree.items;
262         
263         //Roo.log(items.length);
264             // add the items..
265         for(var i =0;i < items.length;i++) {
266             nitems.push(cn.addxtype(Roo.apply({}, items[i])));
267         }
268         
269         cn.items = nitems;
270         
271         return cn;
272     }
273     
274     
275     
276     
277 });
278
279  /*
280  * - LGPL
281  *
282  * Body
283  * 
284  */
285
286 /**
287  * @class Roo.bootstrap.Body
288  * @extends Roo.bootstrap.Component
289  * Bootstrap Body class
290  * 
291  * @constructor
292  * Create a new body
293  * @param {Object} config The config object
294  */
295
296 Roo.bootstrap.Body = function(config){
297     Roo.bootstrap.Body.superclass.constructor.call(this, config);
298     this.el = Roo.get(document.body);
299     if (this.cls && this.cls.length) {
300         Roo.get(document.body).addClass(this.cls);
301     }
302 };
303
304 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
305       
306         autoCreate : {
307         cls: 'container'
308     },
309     onRender : function(ct, position)
310     {
311        /* Roo.log("Roo.bootstrap.Body - onRender");
312         if (this.cls && this.cls.length) {
313             Roo.get(document.body).addClass(this.cls);
314         }
315         // style??? xttr???
316         */
317     }
318     
319     
320  
321    
322 });
323
324  /*
325  * - LGPL
326  *
327  * button group
328  * 
329  */
330
331
332 /**
333  * @class Roo.bootstrap.ButtonGroup
334  * @extends Roo.bootstrap.Component
335  * Bootstrap ButtonGroup class
336  * @cfg {String} size lg | sm | xs (default empty normal)
337  * @cfg {String} align vertical | justified  (default none)
338  * @cfg {String} direction up | down (default down)
339  * @cfg {Boolean} toolbar false | true
340  * @cfg {Boolean} btn true | false
341  * 
342  * 
343  * @constructor
344  * Create a new Input
345  * @param {Object} config The config object
346  */
347
348 Roo.bootstrap.ButtonGroup = function(config){
349     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
350 };
351
352 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
353     
354     size: '',
355     align: '',
356     direction: '',
357     toolbar: false,
358     btn: true,
359
360     getAutoCreate : function(){
361         var cfg = {
362             cls: 'btn-group',
363             html : null
364         }
365         
366         cfg.html = this.html || cfg.html;
367         
368         if (this.toolbar) {
369             cfg = {
370                 cls: 'btn-toolbar',
371                 html: null
372             }
373             
374             return cfg;
375         }
376         
377         if (['vertical','justified'].indexOf(this.align)!==-1) {
378             cfg.cls = 'btn-group-' + this.align;
379             
380             if (this.align == 'justified') {
381                 console.log(this.items);
382             }
383         }
384         
385         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
386             cfg.cls += ' btn-group-' + this.size;
387         }
388         
389         if (this.direction == 'up') {
390             cfg.cls += ' dropup' ;
391         }
392         
393         return cfg;
394     }
395    
396 });
397
398  /*
399  * - LGPL
400  *
401  * button
402  * 
403  */
404
405 /**
406  * @class Roo.bootstrap.Button
407  * @extends Roo.bootstrap.Component
408  * Bootstrap Button class
409  * @cfg {String} html The button content
410  * @cfg {String} weight default (or empty) | primary | success | info | warning | danger | link
411  * @cfg {String} size empty | lg | sm | xs
412  * @cfg {String} tag empty | a | input | submit
413  * @cfg {String} href empty or href
414  * @cfg {Boolean} disabled false | true
415  * @cfg {Boolean} isClose false | true
416  * @cfg {String} glyphicon empty | adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out
417  * @cfg {String} badge text for badge
418  * @cfg {String} theme default (or empty) | glow
419  * @cfg {Boolean} inverse false | true
420  * @cfg {Boolean} toggle false | true
421  * @cfg {String} ontext text for on toggle state
422  * @cfg {String} offtext text for off toggle state
423  * @cfg {Boolean} defaulton true | false
424  * @cfg {Boolean} preventDefault (true | false) default true
425  * @cfg {Boolean} removeClass true | false remove the standard class..
426  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
427  * 
428  * @constructor
429  * Create a new button
430  * @param {Object} config The config object
431  */
432
433
434 Roo.bootstrap.Button = function(config){
435     Roo.bootstrap.Button.superclass.constructor.call(this, config);
436     this.addEvents({
437         // raw events
438         /**
439          * @event click
440          * When a butotn is pressed
441          * @param {Roo.EventObject} e
442          */
443         "click" : true,
444          /**
445          * @event toggle
446          * After the button has been toggles
447          * @param {Roo.EventObject} e
448          * @param {boolean} pressed (also available as button.pressed)
449          */
450         "toggle" : true
451     });
452 };
453
454 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
455     html: false,
456     active: false,
457     weight: '',
458     size: '',
459     tag: 'button',
460     href: '',
461     disabled: false,
462     isClose: false,
463     glyphicon: '',
464     badge: '',
465     theme: 'default',
466     inverse: false,
467     
468     toggle: false,
469     ontext: 'ON',
470     offtext: 'OFF',
471     defaulton: true,
472     preventDefault: true,
473     removeClass: false,
474     name: false,
475     target: false,
476     
477     
478     pressed : null,
479      
480     
481     getAutoCreate : function(){
482         
483         var cfg = {
484             tag : 'button',
485             cls : 'roo-button',
486             html: ''
487         };
488         
489         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
490             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
491             this.tag = 'button';
492         } else {
493             cfg.tag = this.tag;
494         }
495         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
496         
497         if (this.toggle == true) {
498             cfg={
499                 tag: 'div',
500                 cls: 'slider-frame roo-button',
501                 cn: [
502                     {
503                         tag: 'span',
504                         'data-on-text':'ON',
505                         'data-off-text':'OFF',
506                         cls: 'slider-button',
507                         html: this.offtext
508                     }
509                 ]
510             };
511             
512             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
513                 cfg.cls += ' '+this.weight;
514             }
515             
516             return cfg;
517         }
518         
519         if (this.isClose) {
520             cfg.cls += ' close';
521             
522             cfg["aria-hidden"] = true;
523             
524             cfg.html = "&times;";
525             
526             return cfg;
527         }
528         
529          
530         if (this.theme==='default') {
531             cfg.cls = 'btn roo-button';
532             
533             //if (this.parentType != 'Navbar') {
534             this.weight = this.weight.length ?  this.weight : 'default';
535             //}
536             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
537                 
538                 cfg.cls += ' btn-' + this.weight;
539             }
540         } else if (this.theme==='glow') {
541             
542             cfg.tag = 'a';
543             cfg.cls = 'btn-glow roo-button';
544             
545             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
546                 
547                 cfg.cls += ' ' + this.weight;
548             }
549         }
550    
551         
552         if (this.inverse) {
553             this.cls += ' inverse';
554         }
555         
556         
557         if (this.active) {
558             cfg.cls += ' active';
559         }
560         
561         if (this.disabled) {
562             cfg.disabled = 'disabled';
563         }
564         
565         if (this.items) {
566             Roo.log('changing to ul' );
567             cfg.tag = 'ul';
568             this.glyphicon = 'caret';
569         }
570         
571         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
572          
573         //gsRoo.log(this.parentType);
574         if (this.parentType === 'Navbar' && !this.parent().bar) {
575             Roo.log('changing to li?');
576             
577             cfg.tag = 'li';
578             
579             cfg.cls = '';
580             cfg.cn =  [{
581                 tag : 'a',
582                 cls : 'roo-button',
583                 html : this.html,
584                 href : this.href || '#'
585             }];
586             if (this.menu) {
587                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
588                 cfg.cls += ' dropdown';
589             }   
590             
591             delete cfg.html;
592             
593         }
594         
595        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
596         
597         if (this.glyphicon) {
598             cfg.html = ' ' + cfg.html;
599             
600             cfg.cn = [
601                 {
602                     tag: 'span',
603                     cls: 'glyphicon glyphicon-' + this.glyphicon
604                 }
605             ];
606         }
607         
608         if (this.badge) {
609             cfg.html += ' ';
610             
611             cfg.tag = 'a';
612             
613 //            cfg.cls='btn roo-button';
614             
615             cfg.href=this.href;
616             
617             var value = cfg.html;
618             
619             if(this.glyphicon){
620                 value = {
621                             tag: 'span',
622                             cls: 'glyphicon glyphicon-' + this.glyphicon,
623                             html: this.html
624                         };
625                 
626             }
627             
628             cfg.cn = [
629                 value,
630                 {
631                     tag: 'span',
632                     cls: 'badge',
633                     html: this.badge
634                 }
635             ];
636             
637             cfg.html='';
638         }
639         
640         if (this.menu) {
641             cfg.cls += ' dropdown';
642             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
643         }
644         
645         if (cfg.tag !== 'a' && this.href !== '') {
646             throw "Tag must be a to set href.";
647         } else if (this.href.length > 0) {
648             cfg.href = this.href;
649         }
650         
651         if(this.removeClass){
652             cfg.cls = '';
653         }
654         
655         if(this.target){
656             cfg.target = this.target;
657         }
658         
659         return cfg;
660     },
661     initEvents: function() {
662        // Roo.log('init events?');
663 //        Roo.log(this.el.dom);
664         // add the menu...
665         
666         if (typeof (this.menu) != 'undefined') {
667             this.menu.parentType = this.xtype;
668             this.menu.triggerEl = this.el;
669             this.addxtype(Roo.apply({}, this.menu));
670         }
671
672
673        if (this.el.hasClass('roo-button')) {
674             this.el.on('click', this.onClick, this);
675        } else {
676             this.el.select('.roo-button').on('click', this.onClick, this);
677        }
678        
679        if(this.removeClass){
680            this.el.on('click', this.onClick, this);
681        }
682        
683        this.el.enableDisplayMode();
684         
685     },
686     onClick : function(e)
687     {
688         if (this.disabled) {
689             return;
690         }
691         
692         Roo.log('button on click ');
693         if(this.preventDefault){
694             e.preventDefault();
695         }
696         if (this.pressed === true || this.pressed === false) {
697             this.pressed = !this.pressed;
698             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
699             this.fireEvent('toggle', this, e, this.pressed);
700         }
701         
702         
703         this.fireEvent('click', this, e);
704     },
705     
706     /**
707      * Enables this button
708      */
709     enable : function()
710     {
711         this.disabled = false;
712         this.el.removeClass('disabled');
713     },
714     
715     /**
716      * Disable this button
717      */
718     disable : function()
719     {
720         this.disabled = true;
721         this.el.addClass('disabled');
722     },
723      /**
724      * sets the active state on/off, 
725      * @param {Boolean} state (optional) Force a particular state
726      */
727     setActive : function(v) {
728         
729         this.el[v ? 'addClass' : 'removeClass']('active');
730     },
731      /**
732      * toggles the current active state 
733      */
734     toggleActive : function()
735     {
736        var active = this.el.hasClass('active');
737        this.setActive(!active);
738        
739         
740     },
741     setText : function(str)
742     {
743         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
744     },
745     getText : function()
746     {
747         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
748     },
749     hide: function() {
750        
751      
752         this.el.hide();   
753     },
754     show: function() {
755        
756         this.el.show();   
757     }
758     
759     
760 });
761
762  /*
763  * - LGPL
764  *
765  * column
766  * 
767  */
768
769 /**
770  * @class Roo.bootstrap.Column
771  * @extends Roo.bootstrap.Component
772  * Bootstrap Column class
773  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
774  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
775  * @cfg {Number} md colspan out of 12 for computer-sized screens
776  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
777  * @cfg {String} html content of column.
778  * 
779  * @constructor
780  * Create a new Column
781  * @param {Object} config The config object
782  */
783
784 Roo.bootstrap.Column = function(config){
785     Roo.bootstrap.Column.superclass.constructor.call(this, config);
786 };
787
788 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
789     
790     xs: null,
791     sm: null,
792     md: null,
793     lg: null,
794     html: '',
795     offset: 0,
796     
797     getAutoCreate : function(){
798         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
799         
800         cfg = {
801             tag: 'div',
802             cls: 'column'
803         };
804         
805         var settings=this;
806         ['xs','sm','md','lg'].map(function(size){
807             if (settings[size]) {
808                 cfg.cls += ' col-' + size + '-' + settings[size];
809             }
810         });
811         if (this.html.length) {
812             cfg.html = this.html;
813         }
814         
815         return cfg;
816     }
817    
818 });
819
820  
821
822  /*
823  * - LGPL
824  *
825  * page container.
826  * 
827  */
828
829
830 /**
831  * @class Roo.bootstrap.Container
832  * @extends Roo.bootstrap.Component
833  * Bootstrap Container class
834  * @cfg {Boolean} jumbotron is it a jumbotron element
835  * @cfg {String} html content of element
836  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
837  * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
838  * @cfg {String} header content of header (for panel)
839  * @cfg {String} footer content of footer (for panel)
840  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
841  * @cfg {String} tag (header|aside|section) type of HTML tag.
842
843  *     
844  * @constructor
845  * Create a new Container
846  * @param {Object} config The config object
847  */
848
849 Roo.bootstrap.Container = function(config){
850     Roo.bootstrap.Container.superclass.constructor.call(this, config);
851 };
852
853 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
854     
855     jumbotron : false,
856     well: '',
857     panel : '',
858     header: '',
859     footer : '',
860     sticky: '',
861     tag : false,
862   
863      
864     getChildContainer : function() {
865         
866         if(!this.el){
867             return false;
868         }
869         
870         if (this.panel.length) {
871             return this.el.select('.panel-body',true).first();
872         }
873         
874         return this.el;
875     },
876     
877     
878     getAutoCreate : function(){
879         
880         var cfg = {
881             tag : this.tag || 'div',
882             html : '',
883             cls : ''
884         };
885         if (this.jumbotron) {
886             cfg.cls = 'jumbotron';
887         }
888         // - this is applied by the parent..
889         //if (this.cls) {
890         //    cfg.cls = this.cls + '';
891         //}
892         
893         if (this.sticky.length) {
894             
895             var bd = Roo.get(document.body);
896             if (!bd.hasClass('bootstrap-sticky')) {
897                 bd.addClass('bootstrap-sticky');
898                 Roo.select('html',true).setStyle('height', '100%');
899             }
900              
901             cfg.cls += 'bootstrap-sticky-' + this.sticky;
902         }
903         
904         
905         if (this.well.length) {
906             switch (this.well) {
907                 case 'lg':
908                 case 'sm':
909                     cfg.cls +=' well well-' +this.well;
910                     break;
911                 default:
912                     cfg.cls +=' well';
913                     break;
914             }
915         }
916         
917         var body = cfg;
918         
919         if (this.panel.length) {
920             cfg.cls += ' panel panel-' + this.panel;
921             cfg.cn = [];
922             if (this.header.length) {
923                 cfg.cn.push({
924                     
925                     cls : 'panel-heading',
926                     cn : [{
927                         tag: 'h3',
928                         cls : 'panel-title',
929                         html : this.header
930                     }]
931                     
932                 });
933             }
934             body = false;
935             cfg.cn.push({
936                 cls : 'panel-body',
937                 html : this.html
938             });
939             
940             
941             if (this.footer.length) {
942                 cfg.cn.push({
943                     cls : 'panel-footer',
944                     html : this.footer
945                     
946                 });
947             }
948             
949         }
950         
951         if (body) {
952             body.html = this.html || cfg.html;
953         }
954         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
955             cfg.cls =  'container';
956         }
957         
958         return cfg;
959     }
960    
961 });
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             if(typeof(config.hidden) != 'undefined' && config.hidden){
4287                 continue;
4288             }
4289                     
4290             var c = {
4291                 tag: 'th',
4292                 html: cm.getColumnHeader(i)
4293             };
4294             
4295             if(typeof(config.dataIndex) != 'undefined'){
4296                 c.sort = config.dataIndex;
4297             }
4298             
4299             if(typeof(config.sortable) != 'undefined' && config.sortable){
4300                 c.cls = 'sortable';
4301             }
4302             
4303             if(typeof(config.width) != 'undefined'){
4304                 c.style = 'width:' + config.width + 'px';
4305             }
4306             
4307             header.cn.push(c)
4308         }
4309         
4310         return header;
4311     },
4312     
4313     renderBody : function()
4314     {
4315         var body = {
4316             tag: 'tbody',
4317             cn : []
4318         };
4319         
4320         return body;
4321     },
4322     
4323     renderFooter : function()
4324     {
4325         var footer = {
4326             tag: 'tfoot',
4327             cn : []
4328         };
4329         
4330         return footer;
4331     },
4332     
4333     onLoad : function()
4334     {
4335         Roo.log('ds onload');
4336         
4337         var _this = this;
4338         var cm = this.cm;
4339         
4340         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
4341             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
4342             
4343             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
4344                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
4345             }
4346             
4347             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
4348                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
4349             }
4350         });
4351         
4352         var tbody = this.el.select('tbody', true).first();
4353         
4354         var renders = [];
4355                     
4356         if(this.store.getCount() > 0){
4357             this.store.data.each(function(d){
4358                 var row = {
4359                     tag : 'tr',
4360                     cn : []
4361                 };
4362                 
4363                 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
4364                     var config = cm.config[i];
4365                     
4366                     if(typeof(config.hidden) != 'undefined' && config.hidden){
4367                         continue;
4368                     }
4369                     
4370                     var renderer = cm.getRenderer(i);
4371                     var value = '';
4372                     var id = Roo.id();
4373                     
4374                     if(typeof(renderer) !== 'undefined'){
4375                         value = renderer(d.data[cm.getDataIndex(i)], false, d);
4376                     }
4377                     
4378                     if(typeof(value) === 'object'){
4379                         renders.push({
4380                             id : id,
4381                             cfg : value 
4382                         })
4383                     }
4384                     
4385                     var td = {
4386                         tag: 'td',
4387                         id: id,
4388                         html: (typeof(value) === 'object') ? '' : value
4389                     };
4390                     
4391                     if(typeof(config.width) != 'undefined'){
4392                         td.style = 'width:' +  config.width + 'px';
4393                     }
4394                     
4395                     row.cn.push(td);
4396                    
4397                 }
4398                 
4399                 tbody.createChild(row);
4400                 
4401             });
4402         }
4403         
4404         
4405         if(renders.length){
4406             var _this = this;
4407             Roo.each(renders, function(r){
4408                 _this.renderColumn(r);
4409             })
4410         }
4411
4412         if(this.loadMask){
4413             this.maskEl.hide();
4414         }
4415     },
4416     
4417     onBeforeLoad : function()
4418     {
4419         Roo.log('ds onBeforeLoad');
4420         
4421         this.clear();
4422         
4423         if(this.loadMask){
4424             this.maskEl.show();
4425         }
4426     },
4427     
4428     clear : function()
4429     {
4430         this.el.select('tbody', true).first().dom.innerHTML = '';
4431     },
4432     
4433     getSelectionModel : function(){
4434         if(!this.selModel){
4435             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
4436         }
4437         return this.selModel;
4438     },
4439     
4440     renderColumn : function(r)
4441     {
4442         var _this = this;
4443         r.cfg.render(Roo.get(r.id));
4444         
4445         if(r.cfg.cn){
4446             Roo.each(r.cfg.cn, function(c){
4447                 var child = {
4448                     id: r.id,
4449                     cfg: c
4450                 }
4451                 _this.renderColumn(child);
4452             })
4453         }
4454     }
4455    
4456 });
4457
4458  
4459
4460  /*
4461  * - LGPL
4462  *
4463  * table cell
4464  * 
4465  */
4466
4467 /**
4468  * @class Roo.bootstrap.TableCell
4469  * @extends Roo.bootstrap.Component
4470  * Bootstrap TableCell class
4471  * @cfg {String} html cell contain text
4472  * @cfg {String} cls cell class
4473  * @cfg {String} tag cell tag (td|th) default td
4474  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
4475  * @cfg {String} align Aligns the content in a cell
4476  * @cfg {String} axis Categorizes cells
4477  * @cfg {String} bgcolor Specifies the background color of a cell
4478  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
4479  * @cfg {Number} colspan Specifies the number of columns a cell should span
4480  * @cfg {String} headers Specifies one or more header cells a cell is related to
4481  * @cfg {Number} height Sets the height of a cell
4482  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
4483  * @cfg {Number} rowspan Sets the number of rows a cell should span
4484  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
4485  * @cfg {String} valign Vertical aligns the content in a cell
4486  * @cfg {Number} width Specifies the width of a cell
4487  * 
4488  * @constructor
4489  * Create a new TableCell
4490  * @param {Object} config The config object
4491  */
4492
4493 Roo.bootstrap.TableCell = function(config){
4494     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
4495 };
4496
4497 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
4498     
4499     html: false,
4500     cls: false,
4501     tag: false,
4502     abbr: false,
4503     align: false,
4504     axis: false,
4505     bgcolor: false,
4506     charoff: false,
4507     colspan: false,
4508     headers: false,
4509     height: false,
4510     nowrap: false,
4511     rowspan: false,
4512     scope: false,
4513     valign: false,
4514     width: false,
4515     
4516     
4517     getAutoCreate : function(){
4518         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
4519         
4520         cfg = {
4521             tag: 'td'
4522         }
4523         
4524         if(this.tag){
4525             cfg.tag = this.tag;
4526         }
4527         
4528         if (this.html) {
4529             cfg.html=this.html
4530         }
4531         if (this.cls) {
4532             cfg.cls=this.cls
4533         }
4534         if (this.abbr) {
4535             cfg.abbr=this.abbr
4536         }
4537         if (this.align) {
4538             cfg.align=this.align
4539         }
4540         if (this.axis) {
4541             cfg.axis=this.axis
4542         }
4543         if (this.bgcolor) {
4544             cfg.bgcolor=this.bgcolor
4545         }
4546         if (this.charoff) {
4547             cfg.charoff=this.charoff
4548         }
4549         if (this.colspan) {
4550             cfg.colspan=this.colspan
4551         }
4552         if (this.headers) {
4553             cfg.headers=this.headers
4554         }
4555         if (this.height) {
4556             cfg.height=this.height
4557         }
4558         if (this.nowrap) {
4559             cfg.nowrap=this.nowrap
4560         }
4561         if (this.rowspan) {
4562             cfg.rowspan=this.rowspan
4563         }
4564         if (this.scope) {
4565             cfg.scope=this.scope
4566         }
4567         if (this.valign) {
4568             cfg.valign=this.valign
4569         }
4570         if (this.width) {
4571             cfg.width=this.width
4572         }
4573         
4574         
4575         return cfg;
4576     }
4577    
4578 });
4579
4580  
4581
4582  /*
4583  * - LGPL
4584  *
4585  * table row
4586  * 
4587  */
4588
4589 /**
4590  * @class Roo.bootstrap.TableRow
4591  * @extends Roo.bootstrap.Component
4592  * Bootstrap TableRow class
4593  * @cfg {String} cls row class
4594  * @cfg {String} align Aligns the content in a table row
4595  * @cfg {String} bgcolor Specifies a background color for a table row
4596  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
4597  * @cfg {String} valign Vertical aligns the content in a table row
4598  * 
4599  * @constructor
4600  * Create a new TableRow
4601  * @param {Object} config The config object
4602  */
4603
4604 Roo.bootstrap.TableRow = function(config){
4605     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
4606 };
4607
4608 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
4609     
4610     cls: false,
4611     align: false,
4612     bgcolor: false,
4613     charoff: false,
4614     valign: false,
4615     
4616     getAutoCreate : function(){
4617         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
4618         
4619         cfg = {
4620             tag: 'tr'
4621         }
4622             
4623         if(this.cls){
4624             cfg.cls = this.cls;
4625         }
4626         if(this.align){
4627             cfg.align = this.align;
4628         }
4629         if(this.bgcolor){
4630             cfg.bgcolor = this.bgcolor;
4631         }
4632         if(this.charoff){
4633             cfg.charoff = this.charoff;
4634         }
4635         if(this.valign){
4636             cfg.valign = this.valign;
4637         }
4638         
4639         return cfg;
4640     }
4641    
4642 });
4643
4644  
4645
4646  /*
4647  * - LGPL
4648  *
4649  * table body
4650  * 
4651  */
4652
4653 /**
4654  * @class Roo.bootstrap.TableBody
4655  * @extends Roo.bootstrap.Component
4656  * Bootstrap TableBody class
4657  * @cfg {String} cls element class
4658  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
4659  * @cfg {String} align Aligns the content inside the element
4660  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
4661  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
4662  * 
4663  * @constructor
4664  * Create a new TableBody
4665  * @param {Object} config The config object
4666  */
4667
4668 Roo.bootstrap.TableBody = function(config){
4669     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
4670 };
4671
4672 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
4673     
4674     cls: false,
4675     tag: false,
4676     align: false,
4677     charoff: false,
4678     valign: false,
4679     
4680     getAutoCreate : function(){
4681         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
4682         
4683         cfg = {
4684             tag: 'tbody'
4685         }
4686             
4687         if (this.cls) {
4688             cfg.cls=this.cls
4689         }
4690         if(this.tag){
4691             cfg.tag = this.tag;
4692         }
4693         
4694         if(this.align){
4695             cfg.align = this.align;
4696         }
4697         if(this.charoff){
4698             cfg.charoff = this.charoff;
4699         }
4700         if(this.valign){
4701             cfg.valign = this.valign;
4702         }
4703         
4704         return cfg;
4705     }
4706     
4707     
4708 //    initEvents : function()
4709 //    {
4710 //        
4711 //        if(!this.store){
4712 //            return;
4713 //        }
4714 //        
4715 //        this.store = Roo.factory(this.store, Roo.data);
4716 //        this.store.on('load', this.onLoad, this);
4717 //        
4718 //        this.store.load();
4719 //        
4720 //    },
4721 //    
4722 //    onLoad: function () 
4723 //    {   
4724 //        this.fireEvent('load', this);
4725 //    }
4726 //    
4727 //   
4728 });
4729
4730  
4731
4732  /*
4733  * Based on:
4734  * Ext JS Library 1.1.1
4735  * Copyright(c) 2006-2007, Ext JS, LLC.
4736  *
4737  * Originally Released Under LGPL - original licence link has changed is not relivant.
4738  *
4739  * Fork - LGPL
4740  * <script type="text/javascript">
4741  */
4742
4743 // as we use this in bootstrap.
4744 Roo.namespace('Roo.form');
4745  /**
4746  * @class Roo.form.Action
4747  * Internal Class used to handle form actions
4748  * @constructor
4749  * @param {Roo.form.BasicForm} el The form element or its id
4750  * @param {Object} config Configuration options
4751  */
4752
4753  
4754  
4755 // define the action interface
4756 Roo.form.Action = function(form, options){
4757     this.form = form;
4758     this.options = options || {};
4759 };
4760 /**
4761  * Client Validation Failed
4762  * @const 
4763  */
4764 Roo.form.Action.CLIENT_INVALID = 'client';
4765 /**
4766  * Server Validation Failed
4767  * @const 
4768  */
4769 Roo.form.Action.SERVER_INVALID = 'server';
4770  /**
4771  * Connect to Server Failed
4772  * @const 
4773  */
4774 Roo.form.Action.CONNECT_FAILURE = 'connect';
4775 /**
4776  * Reading Data from Server Failed
4777  * @const 
4778  */
4779 Roo.form.Action.LOAD_FAILURE = 'load';
4780
4781 Roo.form.Action.prototype = {
4782     type : 'default',
4783     failureType : undefined,
4784     response : undefined,
4785     result : undefined,
4786
4787     // interface method
4788     run : function(options){
4789
4790     },
4791
4792     // interface method
4793     success : function(response){
4794
4795     },
4796
4797     // interface method
4798     handleResponse : function(response){
4799
4800     },
4801
4802     // default connection failure
4803     failure : function(response){
4804         
4805         this.response = response;
4806         this.failureType = Roo.form.Action.CONNECT_FAILURE;
4807         this.form.afterAction(this, false);
4808     },
4809
4810     processResponse : function(response){
4811         this.response = response;
4812         if(!response.responseText){
4813             return true;
4814         }
4815         this.result = this.handleResponse(response);
4816         return this.result;
4817     },
4818
4819     // utility functions used internally
4820     getUrl : function(appendParams){
4821         var url = this.options.url || this.form.url || this.form.el.dom.action;
4822         if(appendParams){
4823             var p = this.getParams();
4824             if(p){
4825                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
4826             }
4827         }
4828         return url;
4829     },
4830
4831     getMethod : function(){
4832         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
4833     },
4834
4835     getParams : function(){
4836         var bp = this.form.baseParams;
4837         var p = this.options.params;
4838         if(p){
4839             if(typeof p == "object"){
4840                 p = Roo.urlEncode(Roo.applyIf(p, bp));
4841             }else if(typeof p == 'string' && bp){
4842                 p += '&' + Roo.urlEncode(bp);
4843             }
4844         }else if(bp){
4845             p = Roo.urlEncode(bp);
4846         }
4847         return p;
4848     },
4849
4850     createCallback : function(){
4851         return {
4852             success: this.success,
4853             failure: this.failure,
4854             scope: this,
4855             timeout: (this.form.timeout*1000),
4856             upload: this.form.fileUpload ? this.success : undefined
4857         };
4858     }
4859 };
4860
4861 Roo.form.Action.Submit = function(form, options){
4862     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
4863 };
4864
4865 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
4866     type : 'submit',
4867
4868     haveProgress : false,
4869     uploadComplete : false,
4870     
4871     // uploadProgress indicator.
4872     uploadProgress : function()
4873     {
4874         if (!this.form.progressUrl) {
4875             return;
4876         }
4877         
4878         if (!this.haveProgress) {
4879             Roo.MessageBox.progress("Uploading", "Uploading");
4880         }
4881         if (this.uploadComplete) {
4882            Roo.MessageBox.hide();
4883            return;
4884         }
4885         
4886         this.haveProgress = true;
4887    
4888         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
4889         
4890         var c = new Roo.data.Connection();
4891         c.request({
4892             url : this.form.progressUrl,
4893             params: {
4894                 id : uid
4895             },
4896             method: 'GET',
4897             success : function(req){
4898                //console.log(data);
4899                 var rdata = false;
4900                 var edata;
4901                 try  {
4902                    rdata = Roo.decode(req.responseText)
4903                 } catch (e) {
4904                     Roo.log("Invalid data from server..");
4905                     Roo.log(edata);
4906                     return;
4907                 }
4908                 if (!rdata || !rdata.success) {
4909                     Roo.log(rdata);
4910                     Roo.MessageBox.alert(Roo.encode(rdata));
4911                     return;
4912                 }
4913                 var data = rdata.data;
4914                 
4915                 if (this.uploadComplete) {
4916                    Roo.MessageBox.hide();
4917                    return;
4918                 }
4919                    
4920                 if (data){
4921                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
4922                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
4923                     );
4924                 }
4925                 this.uploadProgress.defer(2000,this);
4926             },
4927        
4928             failure: function(data) {
4929                 Roo.log('progress url failed ');
4930                 Roo.log(data);
4931             },
4932             scope : this
4933         });
4934            
4935     },
4936     
4937     
4938     run : function()
4939     {
4940         // run get Values on the form, so it syncs any secondary forms.
4941         this.form.getValues();
4942         
4943         var o = this.options;
4944         var method = this.getMethod();
4945         var isPost = method == 'POST';
4946         if(o.clientValidation === false || this.form.isValid()){
4947             
4948             if (this.form.progressUrl) {
4949                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
4950                     (new Date() * 1) + '' + Math.random());
4951                     
4952             } 
4953             
4954             
4955             Roo.Ajax.request(Roo.apply(this.createCallback(), {
4956                 form:this.form.el.dom,
4957                 url:this.getUrl(!isPost),
4958                 method: method,
4959                 params:isPost ? this.getParams() : null,
4960                 isUpload: this.form.fileUpload
4961             }));
4962             
4963             this.uploadProgress();
4964
4965         }else if (o.clientValidation !== false){ // client validation failed
4966             this.failureType = Roo.form.Action.CLIENT_INVALID;
4967             this.form.afterAction(this, false);
4968         }
4969     },
4970
4971     success : function(response)
4972     {
4973         this.uploadComplete= true;
4974         if (this.haveProgress) {
4975             Roo.MessageBox.hide();
4976         }
4977         
4978         
4979         var result = this.processResponse(response);
4980         if(result === true || result.success){
4981             this.form.afterAction(this, true);
4982             return;
4983         }
4984         if(result.errors){
4985             this.form.markInvalid(result.errors);
4986             this.failureType = Roo.form.Action.SERVER_INVALID;
4987         }
4988         this.form.afterAction(this, false);
4989     },
4990     failure : function(response)
4991     {
4992         this.uploadComplete= true;
4993         if (this.haveProgress) {
4994             Roo.MessageBox.hide();
4995         }
4996         
4997         this.response = response;
4998         this.failureType = Roo.form.Action.CONNECT_FAILURE;
4999         this.form.afterAction(this, false);
5000     },
5001     
5002     handleResponse : function(response){
5003         if(this.form.errorReader){
5004             var rs = this.form.errorReader.read(response);
5005             var errors = [];
5006             if(rs.records){
5007                 for(var i = 0, len = rs.records.length; i < len; i++) {
5008                     var r = rs.records[i];
5009                     errors[i] = r.data;
5010                 }
5011             }
5012             if(errors.length < 1){
5013                 errors = null;
5014             }
5015             return {
5016                 success : rs.success,
5017                 errors : errors
5018             };
5019         }
5020         var ret = false;
5021         try {
5022             ret = Roo.decode(response.responseText);
5023         } catch (e) {
5024             ret = {
5025                 success: false,
5026                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
5027                 errors : []
5028             };
5029         }
5030         return ret;
5031         
5032     }
5033 });
5034
5035
5036 Roo.form.Action.Load = function(form, options){
5037     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
5038     this.reader = this.form.reader;
5039 };
5040
5041 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
5042     type : 'load',
5043
5044     run : function(){
5045         
5046         Roo.Ajax.request(Roo.apply(
5047                 this.createCallback(), {
5048                     method:this.getMethod(),
5049                     url:this.getUrl(false),
5050                     params:this.getParams()
5051         }));
5052     },
5053
5054     success : function(response){
5055         
5056         var result = this.processResponse(response);
5057         if(result === true || !result.success || !result.data){
5058             this.failureType = Roo.form.Action.LOAD_FAILURE;
5059             this.form.afterAction(this, false);
5060             return;
5061         }
5062         this.form.clearInvalid();
5063         this.form.setValues(result.data);
5064         this.form.afterAction(this, true);
5065     },
5066
5067     handleResponse : function(response){
5068         if(this.form.reader){
5069             var rs = this.form.reader.read(response);
5070             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
5071             return {
5072                 success : rs.success,
5073                 data : data
5074             };
5075         }
5076         return Roo.decode(response.responseText);
5077     }
5078 });
5079
5080 Roo.form.Action.ACTION_TYPES = {
5081     'load' : Roo.form.Action.Load,
5082     'submit' : Roo.form.Action.Submit
5083 };/*
5084  * - LGPL
5085  *
5086  * form
5087  * 
5088  */
5089
5090 /**
5091  * @class Roo.bootstrap.Form
5092  * @extends Roo.bootstrap.Component
5093  * Bootstrap Form class
5094  * @cfg {String} method  GET | POST (default POST)
5095  * @cfg {String} labelAlign top | left (default top)
5096   * @cfg {String} align left  | right - for navbars
5097
5098  * 
5099  * @constructor
5100  * Create a new Form
5101  * @param {Object} config The config object
5102  */
5103
5104
5105 Roo.bootstrap.Form = function(config){
5106     Roo.bootstrap.Form.superclass.constructor.call(this, config);
5107     this.addEvents({
5108         /**
5109          * @event clientvalidation
5110          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
5111          * @param {Form} this
5112          * @param {Boolean} valid true if the form has passed client-side validation
5113          */
5114         clientvalidation: true,
5115         /**
5116          * @event beforeaction
5117          * Fires before any action is performed. Return false to cancel the action.
5118          * @param {Form} this
5119          * @param {Action} action The action to be performed
5120          */
5121         beforeaction: true,
5122         /**
5123          * @event actionfailed
5124          * Fires when an action fails.
5125          * @param {Form} this
5126          * @param {Action} action The action that failed
5127          */
5128         actionfailed : true,
5129         /**
5130          * @event actioncomplete
5131          * Fires when an action is completed.
5132          * @param {Form} this
5133          * @param {Action} action The action that completed
5134          */
5135         actioncomplete : true
5136     });
5137     
5138 };
5139
5140 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
5141       
5142      /**
5143      * @cfg {String} method
5144      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
5145      */
5146     method : 'POST',
5147     /**
5148      * @cfg {String} url
5149      * The URL to use for form actions if one isn't supplied in the action options.
5150      */
5151     /**
5152      * @cfg {Boolean} fileUpload
5153      * Set to true if this form is a file upload.
5154      */
5155      
5156     /**
5157      * @cfg {Object} baseParams
5158      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
5159      */
5160       
5161     /**
5162      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
5163      */
5164     timeout: 30,
5165     /**
5166      * @cfg {Sting} align (left|right) for navbar forms
5167      */
5168     align : 'left',
5169
5170     // private
5171     activeAction : null,
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     waitMsgTarget : false,
5179     
5180      
5181     
5182     /**
5183      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
5184      * element by passing it or its id or mask the form itself by passing in true.
5185      * @type Mixed
5186      */
5187     
5188     getAutoCreate : function(){
5189         
5190         var cfg = {
5191             tag: 'form',
5192             method : this.method || 'POST',
5193             id : this.id || Roo.id(),
5194             cls : ''
5195         }
5196         if (this.parent().xtype.match(/^Nav/)) {
5197             cfg.cls = 'navbar-form navbar-' + this.align;
5198             
5199         }
5200         
5201         if (this.labelAlign == 'left' ) {
5202             cfg.cls += ' form-horizontal';
5203         }
5204         
5205         
5206         return cfg;
5207     },
5208     initEvents : function()
5209     {
5210         this.el.on('submit', this.onSubmit, this);
5211         
5212         
5213     },
5214     // private
5215     onSubmit : function(e){
5216         e.stopEvent();
5217     },
5218     
5219      /**
5220      * Returns true if client-side validation on the form is successful.
5221      * @return Boolean
5222      */
5223     isValid : function(){
5224         var items = this.getItems();
5225         var valid = true;
5226         items.each(function(f){
5227            if(!f.validate()){
5228                valid = false;
5229                
5230            }
5231         });
5232         return valid;
5233     },
5234     /**
5235      * Returns true if any fields in this form have changed since their original load.
5236      * @return Boolean
5237      */
5238     isDirty : function(){
5239         var dirty = false;
5240         var items = this.getItems();
5241         items.each(function(f){
5242            if(f.isDirty()){
5243                dirty = true;
5244                return false;
5245            }
5246            return true;
5247         });
5248         return dirty;
5249     },
5250      /**
5251      * Performs a predefined action (submit or load) or custom actions you define on this form.
5252      * @param {String} actionName The name of the action type
5253      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
5254      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
5255      * accept other config options):
5256      * <pre>
5257 Property          Type             Description
5258 ----------------  ---------------  ----------------------------------------------------------------------------------
5259 url               String           The url for the action (defaults to the form's url)
5260 method            String           The form method to use (defaults to the form's method, or POST if not defined)
5261 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
5262 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
5263                                    validate the form on the client (defaults to false)
5264      * </pre>
5265      * @return {BasicForm} this
5266      */
5267     doAction : function(action, options){
5268         if(typeof action == 'string'){
5269             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
5270         }
5271         if(this.fireEvent('beforeaction', this, action) !== false){
5272             this.beforeAction(action);
5273             action.run.defer(100, action);
5274         }
5275         return this;
5276     },
5277     
5278     // private
5279     beforeAction : function(action){
5280         var o = action.options;
5281         
5282         // not really supported yet.. ??
5283         
5284         //if(this.waitMsgTarget === true){
5285             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
5286         //}else if(this.waitMsgTarget){
5287         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
5288         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
5289         //}else {
5290         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
5291        // }
5292          
5293     },
5294
5295     // private
5296     afterAction : function(action, success){
5297         this.activeAction = null;
5298         var o = action.options;
5299         
5300         //if(this.waitMsgTarget === true){
5301             this.el.unmask();
5302         //}else if(this.waitMsgTarget){
5303         //    this.waitMsgTarget.unmask();
5304         //}else{
5305         //    Roo.MessageBox.updateProgress(1);
5306         //    Roo.MessageBox.hide();
5307        // }
5308         // 
5309         if(success){
5310             if(o.reset){
5311                 this.reset();
5312             }
5313             Roo.callback(o.success, o.scope, [this, action]);
5314             this.fireEvent('actioncomplete', this, action);
5315             
5316         }else{
5317             
5318             // failure condition..
5319             // we have a scenario where updates need confirming.
5320             // eg. if a locking scenario exists..
5321             // we look for { errors : { needs_confirm : true }} in the response.
5322             if (
5323                 (typeof(action.result) != 'undefined')  &&
5324                 (typeof(action.result.errors) != 'undefined')  &&
5325                 (typeof(action.result.errors.needs_confirm) != 'undefined')
5326            ){
5327                 var _t = this;
5328                 Roo.log("not supported yet");
5329                  /*
5330                 
5331                 Roo.MessageBox.confirm(
5332                     "Change requires confirmation",
5333                     action.result.errorMsg,
5334                     function(r) {
5335                         if (r != 'yes') {
5336                             return;
5337                         }
5338                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
5339                     }
5340                     
5341                 );
5342                 */
5343                 
5344                 
5345                 return;
5346             }
5347             
5348             Roo.callback(o.failure, o.scope, [this, action]);
5349             // show an error message if no failed handler is set..
5350             if (!this.hasListener('actionfailed')) {
5351                 Roo.log("need to add dialog support");
5352                 /*
5353                 Roo.MessageBox.alert("Error",
5354                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
5355                         action.result.errorMsg :
5356                         "Saving Failed, please check your entries or try again"
5357                 );
5358                 */
5359             }
5360             
5361             this.fireEvent('actionfailed', this, action);
5362         }
5363         
5364     },
5365     /**
5366      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
5367      * @param {String} id The value to search for
5368      * @return Field
5369      */
5370     findField : function(id){
5371         var items = this.getItems();
5372         var field = items.get(id);
5373         if(!field){
5374              items.each(function(f){
5375                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
5376                     field = f;
5377                     return false;
5378                 }
5379                 return true;
5380             });
5381         }
5382         return field || null;
5383     },
5384      /**
5385      * Mark fields in this form invalid in bulk.
5386      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
5387      * @return {BasicForm} this
5388      */
5389     markInvalid : function(errors){
5390         if(errors instanceof Array){
5391             for(var i = 0, len = errors.length; i < len; i++){
5392                 var fieldError = errors[i];
5393                 var f = this.findField(fieldError.id);
5394                 if(f){
5395                     f.markInvalid(fieldError.msg);
5396                 }
5397             }
5398         }else{
5399             var field, id;
5400             for(id in errors){
5401                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
5402                     field.markInvalid(errors[id]);
5403                 }
5404             }
5405         }
5406         //Roo.each(this.childForms || [], function (f) {
5407         //    f.markInvalid(errors);
5408         //});
5409         
5410         return this;
5411     },
5412
5413     /**
5414      * Set values for fields in this form in bulk.
5415      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
5416      * @return {BasicForm} this
5417      */
5418     setValues : function(values){
5419         if(values instanceof Array){ // array of objects
5420             for(var i = 0, len = values.length; i < len; i++){
5421                 var v = values[i];
5422                 var f = this.findField(v.id);
5423                 if(f){
5424                     f.setValue(v.value);
5425                     if(this.trackResetOnLoad){
5426                         f.originalValue = f.getValue();
5427                     }
5428                 }
5429             }
5430         }else{ // object hash
5431             var field, id;
5432             for(id in values){
5433                 if(typeof values[id] != 'function' && (field = this.findField(id))){
5434                     
5435                     if (field.setFromData && 
5436                         field.valueField && 
5437                         field.displayField &&
5438                         // combos' with local stores can 
5439                         // be queried via setValue()
5440                         // to set their value..
5441                         (field.store && !field.store.isLocal)
5442                         ) {
5443                         // it's a combo
5444                         var sd = { };
5445                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
5446                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
5447                         field.setFromData(sd);
5448                         
5449                     } else {
5450                         field.setValue(values[id]);
5451                     }
5452                     
5453                     
5454                     if(this.trackResetOnLoad){
5455                         field.originalValue = field.getValue();
5456                     }
5457                 }
5458             }
5459         }
5460          
5461         //Roo.each(this.childForms || [], function (f) {
5462         //    f.setValues(values);
5463         //});
5464                 
5465         return this;
5466     },
5467
5468     /**
5469      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
5470      * they are returned as an array.
5471      * @param {Boolean} asString
5472      * @return {Object}
5473      */
5474     getValues : function(asString){
5475         //if (this.childForms) {
5476             // copy values from the child forms
5477         //    Roo.each(this.childForms, function (f) {
5478         //        this.setValues(f.getValues());
5479         //    }, this);
5480         //}
5481         
5482         
5483         
5484         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
5485         if(asString === true){
5486             return fs;
5487         }
5488         return Roo.urlDecode(fs);
5489     },
5490     
5491     /**
5492      * Returns the fields in this form as an object with key/value pairs. 
5493      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
5494      * @return {Object}
5495      */
5496     getFieldValues : function(with_hidden)
5497     {
5498         var items = this.getItems();
5499         var ret = {};
5500         items.each(function(f){
5501             if (!f.getName()) {
5502                 return;
5503             }
5504             var v = f.getValue();
5505             if (f.inputType =='radio') {
5506                 if (typeof(ret[f.getName()]) == 'undefined') {
5507                     ret[f.getName()] = ''; // empty..
5508                 }
5509                 
5510                 if (!f.el.dom.checked) {
5511                     return;
5512                     
5513                 }
5514                 v = f.el.dom.value;
5515                 
5516             }
5517             
5518             // not sure if this supported any more..
5519             if ((typeof(v) == 'object') && f.getRawValue) {
5520                 v = f.getRawValue() ; // dates..
5521             }
5522             // combo boxes where name != hiddenName...
5523             if (f.name != f.getName()) {
5524                 ret[f.name] = f.getRawValue();
5525             }
5526             ret[f.getName()] = v;
5527         });
5528         
5529         return ret;
5530     },
5531
5532     /**
5533      * Clears all invalid messages in this form.
5534      * @return {BasicForm} this
5535      */
5536     clearInvalid : function(){
5537         var items = this.getItems();
5538         
5539         items.each(function(f){
5540            f.clearInvalid();
5541         });
5542         
5543         
5544         
5545         return this;
5546     },
5547
5548     /**
5549      * Resets this form.
5550      * @return {BasicForm} this
5551      */
5552     reset : function(){
5553         var items = this.getItems();
5554         items.each(function(f){
5555             f.reset();
5556         });
5557         
5558         Roo.each(this.childForms || [], function (f) {
5559             f.reset();
5560         });
5561        
5562         
5563         return this;
5564     },
5565     getItems : function()
5566     {
5567         var r=new Roo.util.MixedCollection(false, function(o){
5568             return o.id || (o.id = Roo.id());
5569         });
5570         var iter = function(el) {
5571             if (el.inputEl) {
5572                 r.add(el);
5573             }
5574             if (!el.items) {
5575                 return;
5576             }
5577             Roo.each(el.items,function(e) {
5578                 iter(e);
5579             });
5580             
5581             
5582         };
5583         iter(this);
5584         return r;
5585         
5586         
5587         
5588         
5589     }
5590     
5591 });
5592
5593  
5594 /*
5595  * Based on:
5596  * Ext JS Library 1.1.1
5597  * Copyright(c) 2006-2007, Ext JS, LLC.
5598  *
5599  * Originally Released Under LGPL - original licence link has changed is not relivant.
5600  *
5601  * Fork - LGPL
5602  * <script type="text/javascript">
5603  */
5604 /**
5605  * @class Roo.form.VTypes
5606  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
5607  * @singleton
5608  */
5609 Roo.form.VTypes = function(){
5610     // closure these in so they are only created once.
5611     var alpha = /^[a-zA-Z_]+$/;
5612     var alphanum = /^[a-zA-Z0-9_]+$/;
5613     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
5614     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
5615
5616     // All these messages and functions are configurable
5617     return {
5618         /**
5619          * The function used to validate email addresses
5620          * @param {String} value The email address
5621          */
5622         'email' : function(v){
5623             return email.test(v);
5624         },
5625         /**
5626          * The error text to display when the email validation function returns false
5627          * @type String
5628          */
5629         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
5630         /**
5631          * The keystroke filter mask to be applied on email input
5632          * @type RegExp
5633          */
5634         'emailMask' : /[a-z0-9_\.\-@]/i,
5635
5636         /**
5637          * The function used to validate URLs
5638          * @param {String} value The URL
5639          */
5640         'url' : function(v){
5641             return url.test(v);
5642         },
5643         /**
5644          * The error text to display when the url validation function returns false
5645          * @type String
5646          */
5647         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
5648         
5649         /**
5650          * The function used to validate alpha values
5651          * @param {String} value The value
5652          */
5653         'alpha' : function(v){
5654             return alpha.test(v);
5655         },
5656         /**
5657          * The error text to display when the alpha validation function returns false
5658          * @type String
5659          */
5660         'alphaText' : 'This field should only contain letters and _',
5661         /**
5662          * The keystroke filter mask to be applied on alpha input
5663          * @type RegExp
5664          */
5665         'alphaMask' : /[a-z_]/i,
5666
5667         /**
5668          * The function used to validate alphanumeric values
5669          * @param {String} value The value
5670          */
5671         'alphanum' : function(v){
5672             return alphanum.test(v);
5673         },
5674         /**
5675          * The error text to display when the alphanumeric validation function returns false
5676          * @type String
5677          */
5678         'alphanumText' : 'This field should only contain letters, numbers and _',
5679         /**
5680          * The keystroke filter mask to be applied on alphanumeric input
5681          * @type RegExp
5682          */
5683         'alphanumMask' : /[a-z0-9_]/i
5684     };
5685 }();/*
5686  * - LGPL
5687  *
5688  * Input
5689  * 
5690  */
5691
5692 /**
5693  * @class Roo.bootstrap.Input
5694  * @extends Roo.bootstrap.Component
5695  * Bootstrap Input class
5696  * @cfg {Boolean} disabled is it disabled
5697  * @cfg {String} fieldLabel - the label associated
5698  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
5699  * @cfg {String} name name of the input
5700  * @cfg {string} fieldLabel - the label associated
5701  * @cfg {string}  inputType - input / file submit ...
5702  * @cfg {string} placeholder - placeholder to put in text.
5703  * @cfg {string}  before - input group add on before
5704  * @cfg {string} after - input group add on after
5705  * @cfg {string} size - (lg|sm) or leave empty..
5706  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
5707  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
5708  * @cfg {Number} md colspan out of 12 for computer-sized screens
5709  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
5710  * @cfg {string} value default value of the input
5711  * @cfg {Number} labelWidth set the width of label (0-12)
5712  * @cfg {String} labelAlign (top|left)
5713  * @cfg {Boolean} readOnly Specifies that the field should be read-only
5714  * 
5715  * 
5716  * @constructor
5717  * Create a new Input
5718  * @param {Object} config The config object
5719  */
5720
5721 Roo.bootstrap.Input = function(config){
5722     Roo.bootstrap.Input.superclass.constructor.call(this, config);
5723    
5724         this.addEvents({
5725             /**
5726              * @event focus
5727              * Fires when this field receives input focus.
5728              * @param {Roo.form.Field} this
5729              */
5730             focus : true,
5731             /**
5732              * @event blur
5733              * Fires when this field loses input focus.
5734              * @param {Roo.form.Field} this
5735              */
5736             blur : true,
5737             /**
5738              * @event specialkey
5739              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
5740              * {@link Roo.EventObject#getKey} to determine which key was pressed.
5741              * @param {Roo.form.Field} this
5742              * @param {Roo.EventObject} e The event object
5743              */
5744             specialkey : true,
5745             /**
5746              * @event change
5747              * Fires just before the field blurs if the field value has changed.
5748              * @param {Roo.form.Field} this
5749              * @param {Mixed} newValue The new value
5750              * @param {Mixed} oldValue The original value
5751              */
5752             change : true,
5753             /**
5754              * @event invalid
5755              * Fires after the field has been marked as invalid.
5756              * @param {Roo.form.Field} this
5757              * @param {String} msg The validation message
5758              */
5759             invalid : true,
5760             /**
5761              * @event valid
5762              * Fires after the field has been validated with no errors.
5763              * @param {Roo.form.Field} this
5764              */
5765             valid : true,
5766              /**
5767              * @event keyup
5768              * Fires after the key up
5769              * @param {Roo.form.Field} this
5770              * @param {Roo.EventObject}  e The event Object
5771              */
5772             keyup : true
5773         });
5774 };
5775
5776 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
5777      /**
5778      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
5779       automatic validation (defaults to "keyup").
5780      */
5781     validationEvent : "keyup",
5782      /**
5783      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
5784      */
5785     validateOnBlur : true,
5786     /**
5787      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
5788      */
5789     validationDelay : 250,
5790      /**
5791      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
5792      */
5793     focusClass : "x-form-focus",  // not needed???
5794     
5795        
5796     /**
5797      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
5798      */
5799     invalidClass : "has-error",
5800     
5801     /**
5802      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
5803      */
5804     selectOnFocus : false,
5805     
5806      /**
5807      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
5808      */
5809     maskRe : null,
5810        /**
5811      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
5812      */
5813     vtype : null,
5814     
5815       /**
5816      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
5817      */
5818     disableKeyFilter : false,
5819     
5820        /**
5821      * @cfg {Boolean} disabled True to disable the field (defaults to false).
5822      */
5823     disabled : false,
5824      /**
5825      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
5826      */
5827     allowBlank : true,
5828     /**
5829      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
5830      */
5831     blankText : "This field is required",
5832     
5833      /**
5834      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
5835      */
5836     minLength : 0,
5837     /**
5838      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
5839      */
5840     maxLength : Number.MAX_VALUE,
5841     /**
5842      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
5843      */
5844     minLengthText : "The minimum length for this field is {0}",
5845     /**
5846      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
5847      */
5848     maxLengthText : "The maximum length for this field is {0}",
5849   
5850     
5851     /**
5852      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
5853      * If available, this function will be called only after the basic validators all return true, and will be passed the
5854      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
5855      */
5856     validator : null,
5857     /**
5858      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
5859      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
5860      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
5861      */
5862     regex : null,
5863     /**
5864      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
5865      */
5866     regexText : "",
5867     
5868     
5869     
5870     fieldLabel : '',
5871     inputType : 'text',
5872     
5873     name : false,
5874     placeholder: false,
5875     before : false,
5876     after : false,
5877     size : false,
5878     // private
5879     hasFocus : false,
5880     preventMark: false,
5881     isFormField : true,
5882     value : '',
5883     labelWidth : 2,
5884     labelAlign : false,
5885     readOnly : false,
5886     
5887     parentLabelAlign : function()
5888     {
5889         var parent = this;
5890         while (parent.parent()) {
5891             parent = parent.parent();
5892             if (typeof(parent.labelAlign) !='undefined') {
5893                 return parent.labelAlign;
5894             }
5895         }
5896         return 'left';
5897         
5898     },
5899     
5900     getAutoCreate : function(){
5901         
5902         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
5903         
5904         var id = Roo.id();
5905         
5906         var cfg = {};
5907         
5908         if(this.inputType != 'hidden'){
5909             cfg.cls = 'form-group' //input-group
5910         }
5911         
5912         var input =  {
5913             tag: 'input',
5914             id : id,
5915             type : this.inputType,
5916             value : this.value,
5917             cls : 'form-control',
5918             placeholder : this.placeholder || ''
5919             
5920         };
5921         
5922         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
5923             input.maxLength = this.maxLength;
5924         }
5925         
5926         if (this.disabled) {
5927             input.disabled=true;
5928         }
5929         
5930         if (this.readOnly) {
5931             input.readonly=true;
5932         }
5933         
5934         if (this.name) {
5935             input.name = this.name;
5936         }
5937         if (this.size) {
5938             input.cls += ' input-' + this.size;
5939         }
5940         var settings=this;
5941         ['xs','sm','md','lg'].map(function(size){
5942             if (settings[size]) {
5943                 cfg.cls += ' col-' + size + '-' + settings[size];
5944             }
5945         });
5946         
5947         var inputblock = input;
5948         
5949         if (this.before || this.after) {
5950             
5951             inputblock = {
5952                 cls : 'input-group',
5953                 cn :  [] 
5954             };
5955             if (this.before && typeof(this.before) == 'string') {
5956                 
5957                 inputblock.cn.push({
5958                     tag :'span',
5959                     cls : 'roo-input-before input-group-addon',
5960                     html : this.before
5961                 });
5962             }
5963             if (this.before && typeof(this.before) == 'object') {
5964                 this.before = Roo.factory(this.before);
5965                 Roo.log(this.before);
5966                 inputblock.cn.push({
5967                     tag :'span',
5968                     cls : 'roo-input-before input-group-' +
5969                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
5970                 });
5971             }
5972             
5973             inputblock.cn.push(input);
5974             
5975             if (this.after && typeof(this.after) == 'string') {
5976                 inputblock.cn.push({
5977                     tag :'span',
5978                     cls : 'roo-input-after input-group-addon',
5979                     html : this.after
5980                 });
5981             }
5982             if (this.after && typeof(this.after) == 'object') {
5983                 this.after = Roo.factory(this.after);
5984                 Roo.log(this.after);
5985                 inputblock.cn.push({
5986                     tag :'span',
5987                     cls : 'roo-input-after input-group-' +
5988                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
5989                 });
5990             }
5991         };
5992         
5993         if (align ==='left' && this.fieldLabel.length) {
5994                 Roo.log("left and has label");
5995                 cfg.cn = [
5996                     
5997                     {
5998                         tag: 'label',
5999                         'for' :  id,
6000                         cls : 'control-label col-sm-' + this.labelWidth,
6001                         html : this.fieldLabel
6002                         
6003                     },
6004                     {
6005                         cls : "col-sm-" + (12 - this.labelWidth), 
6006                         cn: [
6007                             inputblock
6008                         ]
6009                     }
6010                     
6011                 ];
6012         } else if ( this.fieldLabel.length) {
6013                 Roo.log(" label");
6014                  cfg.cn = [
6015                    
6016                     {
6017                         tag: 'label',
6018                         //cls : 'input-group-addon',
6019                         html : this.fieldLabel
6020                         
6021                     },
6022                     
6023                     inputblock
6024                     
6025                 ];
6026
6027         } else {
6028             
6029                 Roo.log(" no label && no align");
6030                 cfg.cn = [
6031                     
6032                         inputblock
6033                     
6034                 ];
6035                 
6036                 
6037         };
6038         Roo.log('input-parentType: ' + this.parentType);
6039         
6040         if (this.parentType === 'Navbar' &&  this.parent().bar) {
6041            cfg.cls += ' navbar-form';
6042            Roo.log(cfg);
6043         }
6044         
6045         return cfg;
6046         
6047     },
6048     /**
6049      * return the real input element.
6050      */
6051     inputEl: function ()
6052     {
6053         return this.el.select('input.form-control',true).first();
6054     },
6055     setDisabled : function(v)
6056     {
6057         var i  = this.inputEl().dom;
6058         if (!v) {
6059             i.removeAttribute('disabled');
6060             return;
6061             
6062         }
6063         i.setAttribute('disabled','true');
6064     },
6065     initEvents : function()
6066     {
6067         
6068         this.inputEl().on("keydown" , this.fireKey,  this);
6069         this.inputEl().on("focus", this.onFocus,  this);
6070         this.inputEl().on("blur", this.onBlur,  this);
6071         
6072         this.inputEl().relayEvent('keyup', this);
6073
6074         // reference to original value for reset
6075         this.originalValue = this.getValue();
6076         //Roo.form.TextField.superclass.initEvents.call(this);
6077         if(this.validationEvent == 'keyup'){
6078             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
6079             this.inputEl().on('keyup', this.filterValidation, this);
6080         }
6081         else if(this.validationEvent !== false){
6082             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
6083         }
6084         
6085         if(this.selectOnFocus){
6086             this.on("focus", this.preFocus, this);
6087             
6088         }
6089         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
6090             this.inputEl().on("keypress", this.filterKeys, this);
6091         }
6092        /* if(this.grow){
6093             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
6094             this.el.on("click", this.autoSize,  this);
6095         }
6096         */
6097         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
6098             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
6099         }
6100         
6101         if (typeof(this.before) == 'object') {
6102             this.before.render(this.el.select('.roo-input-before',true).first());
6103         }
6104         if (typeof(this.after) == 'object') {
6105             this.after.render(this.el.select('.roo-input-after',true).first());
6106         }
6107         
6108         
6109     },
6110     filterValidation : function(e){
6111         if(!e.isNavKeyPress()){
6112             this.validationTask.delay(this.validationDelay);
6113         }
6114     },
6115      /**
6116      * Validates the field value
6117      * @return {Boolean} True if the value is valid, else false
6118      */
6119     validate : function(){
6120         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
6121         if(this.disabled || this.validateValue(this.getRawValue())){
6122             this.clearInvalid();
6123             return true;
6124         }
6125         return false;
6126     },
6127     
6128     
6129     /**
6130      * Validates a value according to the field's validation rules and marks the field as invalid
6131      * if the validation fails
6132      * @param {Mixed} value The value to validate
6133      * @return {Boolean} True if the value is valid, else false
6134      */
6135     validateValue : function(value){
6136         if(value.length < 1)  { // if it's blank
6137              if(this.allowBlank){
6138                 this.clearInvalid();
6139                 return true;
6140              }else{
6141                 this.markInvalid(this.blankText);
6142                 return false;
6143              }
6144         }
6145         if(value.length < this.minLength){
6146             this.markInvalid(String.format(this.minLengthText, this.minLength));
6147             return false;
6148         }
6149         if(value.length > this.maxLength){
6150             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
6151             return false;
6152         }
6153         if(this.vtype){
6154             var vt = Roo.form.VTypes;
6155             if(!vt[this.vtype](value, this)){
6156                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
6157                 return false;
6158             }
6159         }
6160         if(typeof this.validator == "function"){
6161             var msg = this.validator(value);
6162             if(msg !== true){
6163                 this.markInvalid(msg);
6164                 return false;
6165             }
6166         }
6167         if(this.regex && !this.regex.test(value)){
6168             this.markInvalid(this.regexText);
6169             return false;
6170         }
6171         return true;
6172     },
6173
6174     
6175     
6176      // private
6177     fireKey : function(e){
6178         //Roo.log('field ' + e.getKey());
6179         if(e.isNavKeyPress()){
6180             this.fireEvent("specialkey", this, e);
6181         }
6182     },
6183     focus : function (selectText){
6184         if(this.rendered){
6185             this.inputEl().focus();
6186             if(selectText === true){
6187                 this.inputEl().dom.select();
6188             }
6189         }
6190         return this;
6191     } ,
6192     
6193     onFocus : function(){
6194         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
6195            // this.el.addClass(this.focusClass);
6196         }
6197         if(!this.hasFocus){
6198             this.hasFocus = true;
6199             this.startValue = this.getValue();
6200             this.fireEvent("focus", this);
6201         }
6202     },
6203     
6204     beforeBlur : Roo.emptyFn,
6205
6206     
6207     // private
6208     onBlur : function(){
6209         this.beforeBlur();
6210         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
6211             //this.el.removeClass(this.focusClass);
6212         }
6213         this.hasFocus = false;
6214         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
6215             this.validate();
6216         }
6217         var v = this.getValue();
6218         if(String(v) !== String(this.startValue)){
6219             this.fireEvent('change', this, v, this.startValue);
6220         }
6221         this.fireEvent("blur", this);
6222     },
6223     
6224     /**
6225      * Resets the current field value to the originally loaded value and clears any validation messages
6226      */
6227     reset : function(){
6228         this.setValue(this.originalValue);
6229         this.clearInvalid();
6230     },
6231      /**
6232      * Returns the name of the field
6233      * @return {Mixed} name The name field
6234      */
6235     getName: function(){
6236         return this.name;
6237     },
6238      /**
6239      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
6240      * @return {Mixed} value The field value
6241      */
6242     getValue : function(){
6243         return this.inputEl().getValue();
6244     },
6245     /**
6246      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
6247      * @return {Mixed} value The field value
6248      */
6249     getRawValue : function(){
6250         var v = this.inputEl().getValue();
6251         
6252         return v;
6253     },
6254     
6255     /**
6256      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
6257      * @param {Mixed} value The value to set
6258      */
6259     setRawValue : function(v){
6260         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
6261     },
6262     
6263     selectText : function(start, end){
6264         var v = this.getRawValue();
6265         if(v.length > 0){
6266             start = start === undefined ? 0 : start;
6267             end = end === undefined ? v.length : end;
6268             var d = this.inputEl().dom;
6269             if(d.setSelectionRange){
6270                 d.setSelectionRange(start, end);
6271             }else if(d.createTextRange){
6272                 var range = d.createTextRange();
6273                 range.moveStart("character", start);
6274                 range.moveEnd("character", v.length-end);
6275                 range.select();
6276             }
6277         }
6278     },
6279     
6280     /**
6281      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
6282      * @param {Mixed} value The value to set
6283      */
6284     setValue : function(v){
6285         this.value = v;
6286         if(this.rendered){
6287             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
6288             this.validate();
6289         }
6290     },
6291     
6292     /*
6293     processValue : function(value){
6294         if(this.stripCharsRe){
6295             var newValue = value.replace(this.stripCharsRe, '');
6296             if(newValue !== value){
6297                 this.setRawValue(newValue);
6298                 return newValue;
6299             }
6300         }
6301         return value;
6302     },
6303   */
6304     preFocus : function(){
6305         
6306         if(this.selectOnFocus){
6307             this.inputEl().dom.select();
6308         }
6309     },
6310     filterKeys : function(e){
6311         var k = e.getKey();
6312         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
6313             return;
6314         }
6315         var c = e.getCharCode(), cc = String.fromCharCode(c);
6316         if(Roo.isIE && (e.isSpecialKey() || !cc)){
6317             return;
6318         }
6319         if(!this.maskRe.test(cc)){
6320             e.stopEvent();
6321         }
6322     },
6323      /**
6324      * Clear any invalid styles/messages for this field
6325      */
6326     clearInvalid : function(){
6327         
6328         if(!this.el || this.preventMark){ // not rendered
6329             return;
6330         }
6331         this.el.removeClass(this.invalidClass);
6332         /*
6333         switch(this.msgTarget){
6334             case 'qtip':
6335                 this.el.dom.qtip = '';
6336                 break;
6337             case 'title':
6338                 this.el.dom.title = '';
6339                 break;
6340             case 'under':
6341                 if(this.errorEl){
6342                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
6343                 }
6344                 break;
6345             case 'side':
6346                 if(this.errorIcon){
6347                     this.errorIcon.dom.qtip = '';
6348                     this.errorIcon.hide();
6349                     this.un('resize', this.alignErrorIcon, this);
6350                 }
6351                 break;
6352             default:
6353                 var t = Roo.getDom(this.msgTarget);
6354                 t.innerHTML = '';
6355                 t.style.display = 'none';
6356                 break;
6357         }
6358         */
6359         this.fireEvent('valid', this);
6360     },
6361      /**
6362      * Mark this field as invalid
6363      * @param {String} msg The validation message
6364      */
6365     markInvalid : function(msg){
6366         if(!this.el  || this.preventMark){ // not rendered
6367             return;
6368         }
6369         this.el.addClass(this.invalidClass);
6370         /*
6371         msg = msg || this.invalidText;
6372         switch(this.msgTarget){
6373             case 'qtip':
6374                 this.el.dom.qtip = msg;
6375                 this.el.dom.qclass = 'x-form-invalid-tip';
6376                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
6377                     Roo.QuickTips.enable();
6378                 }
6379                 break;
6380             case 'title':
6381                 this.el.dom.title = msg;
6382                 break;
6383             case 'under':
6384                 if(!this.errorEl){
6385                     var elp = this.el.findParent('.x-form-element', 5, true);
6386                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
6387                     this.errorEl.setWidth(elp.getWidth(true)-20);
6388                 }
6389                 this.errorEl.update(msg);
6390                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
6391                 break;
6392             case 'side':
6393                 if(!this.errorIcon){
6394                     var elp = this.el.findParent('.x-form-element', 5, true);
6395                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
6396                 }
6397                 this.alignErrorIcon();
6398                 this.errorIcon.dom.qtip = msg;
6399                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
6400                 this.errorIcon.show();
6401                 this.on('resize', this.alignErrorIcon, this);
6402                 break;
6403             default:
6404                 var t = Roo.getDom(this.msgTarget);
6405                 t.innerHTML = msg;
6406                 t.style.display = this.msgDisplay;
6407                 break;
6408         }
6409         */
6410         this.fireEvent('invalid', this, msg);
6411     },
6412     // private
6413     SafariOnKeyDown : function(event)
6414     {
6415         // this is a workaround for a password hang bug on chrome/ webkit.
6416         
6417         var isSelectAll = false;
6418         
6419         if(this.inputEl().dom.selectionEnd > 0){
6420             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
6421         }
6422         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
6423             event.preventDefault();
6424             this.setValue('');
6425             return;
6426         }
6427         
6428         if(isSelectAll){ // backspace and delete key
6429             
6430             event.preventDefault();
6431             // this is very hacky as keydown always get's upper case.
6432             //
6433             var cc = String.fromCharCode(event.getCharCode());
6434             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
6435             
6436         }
6437     },
6438     adjustWidth : function(tag, w){
6439         tag = tag.toLowerCase();
6440         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
6441             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
6442                 if(tag == 'input'){
6443                     return w + 2;
6444                 }
6445                 if(tag == 'textarea'){
6446                     return w-2;
6447                 }
6448             }else if(Roo.isOpera){
6449                 if(tag == 'input'){
6450                     return w + 2;
6451                 }
6452                 if(tag == 'textarea'){
6453                     return w-2;
6454                 }
6455             }
6456         }
6457         return w;
6458     }
6459     
6460 });
6461
6462  
6463 /*
6464  * - LGPL
6465  *
6466  * Input
6467  * 
6468  */
6469
6470 /**
6471  * @class Roo.bootstrap.TextArea
6472  * @extends Roo.bootstrap.Input
6473  * Bootstrap TextArea class
6474  * @cfg {Number} cols Specifies the visible width of a text area
6475  * @cfg {Number} rows Specifies the visible number of lines in a text area
6476  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
6477  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
6478  * @cfg {string} html text
6479  * 
6480  * @constructor
6481  * Create a new TextArea
6482  * @param {Object} config The config object
6483  */
6484
6485 Roo.bootstrap.TextArea = function(config){
6486     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
6487    
6488 };
6489
6490 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
6491      
6492     cols : false,
6493     rows : 5,
6494     readOnly : false,
6495     warp : 'soft',
6496     resize : false,
6497     value: false,
6498     html: false,
6499     
6500     getAutoCreate : function(){
6501         
6502         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
6503         
6504         var id = Roo.id();
6505         
6506         var cfg = {};
6507         
6508         var input =  {
6509             tag: 'textarea',
6510             id : id,
6511             warp : this.warp,
6512             rows : this.rows,
6513             value : this.value || '',
6514             html: this.html || '',
6515             cls : 'form-control',
6516             placeholder : this.placeholder || '' 
6517             
6518         };
6519         
6520         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
6521             input.maxLength = this.maxLength;
6522         }
6523         
6524         if(this.resize){
6525             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
6526         }
6527         
6528         if(this.cols){
6529             input.cols = this.cols;
6530         }
6531         
6532         if (this.readOnly) {
6533             input.readonly = true;
6534         }
6535         
6536         if (this.name) {
6537             input.name = this.name;
6538         }
6539         
6540         if (this.size) {
6541             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
6542         }
6543         
6544         var settings=this;
6545         ['xs','sm','md','lg'].map(function(size){
6546             if (settings[size]) {
6547                 cfg.cls += ' col-' + size + '-' + settings[size];
6548             }
6549         });
6550         
6551         var inputblock = input;
6552         
6553         if (this.before || this.after) {
6554             
6555             inputblock = {
6556                 cls : 'input-group',
6557                 cn :  [] 
6558             };
6559             if (this.before) {
6560                 inputblock.cn.push({
6561                     tag :'span',
6562                     cls : 'input-group-addon',
6563                     html : this.before
6564                 });
6565             }
6566             inputblock.cn.push(input);
6567             if (this.after) {
6568                 inputblock.cn.push({
6569                     tag :'span',
6570                     cls : 'input-group-addon',
6571                     html : this.after
6572                 });
6573             }
6574             
6575         }
6576         
6577         if (align ==='left' && this.fieldLabel.length) {
6578                 Roo.log("left and has label");
6579                 cfg.cn = [
6580                     
6581                     {
6582                         tag: 'label',
6583                         'for' :  id,
6584                         cls : 'control-label col-sm-' + this.labelWidth,
6585                         html : this.fieldLabel
6586                         
6587                     },
6588                     {
6589                         cls : "col-sm-" + (12 - this.labelWidth), 
6590                         cn: [
6591                             inputblock
6592                         ]
6593                     }
6594                     
6595                 ];
6596         } else if ( this.fieldLabel.length) {
6597                 Roo.log(" label");
6598                  cfg.cn = [
6599                    
6600                     {
6601                         tag: 'label',
6602                         //cls : 'input-group-addon',
6603                         html : this.fieldLabel
6604                         
6605                     },
6606                     
6607                     inputblock
6608                     
6609                 ];
6610
6611         } else {
6612             
6613                    Roo.log(" no label && no align");
6614                 cfg.cn = [
6615                     
6616                         inputblock
6617                     
6618                 ];
6619                 
6620                 
6621         }
6622         
6623         if (this.disabled) {
6624             input.disabled=true;
6625         }
6626         
6627         return cfg;
6628         
6629     },
6630     /**
6631      * return the real textarea element.
6632      */
6633     inputEl: function ()
6634     {
6635         return this.el.select('textarea.form-control',true).first();
6636     }
6637 });
6638
6639  
6640 /*
6641  * - LGPL
6642  *
6643  * trigger field - base class for combo..
6644  * 
6645  */
6646  
6647 /**
6648  * @class Roo.bootstrap.TriggerField
6649  * @extends Roo.bootstrap.Input
6650  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
6651  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
6652  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
6653  * for which you can provide a custom implementation.  For example:
6654  * <pre><code>
6655 var trigger = new Roo.bootstrap.TriggerField();
6656 trigger.onTriggerClick = myTriggerFn;
6657 trigger.applyTo('my-field');
6658 </code></pre>
6659  *
6660  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
6661  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
6662  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
6663  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
6664  * @constructor
6665  * Create a new TriggerField.
6666  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
6667  * to the base TextField)
6668  */
6669 Roo.bootstrap.TriggerField = function(config){
6670     this.mimicing = false;
6671     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
6672 };
6673
6674 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
6675     /**
6676      * @cfg {String} triggerClass A CSS class to apply to the trigger
6677      */
6678      /**
6679      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
6680      */
6681     hideTrigger:false,
6682
6683     /** @cfg {Boolean} grow @hide */
6684     /** @cfg {Number} growMin @hide */
6685     /** @cfg {Number} growMax @hide */
6686
6687     /**
6688      * @hide 
6689      * @method
6690      */
6691     autoSize: Roo.emptyFn,
6692     // private
6693     monitorTab : true,
6694     // private
6695     deferHeight : true,
6696
6697     
6698     actionMode : 'wrap',
6699     
6700     
6701     
6702     getAutoCreate : function(){
6703        
6704         var parent = this.parent();
6705         
6706         var align = this.labelAlign || this.parentLabelAlign();
6707         
6708         var id = Roo.id();
6709         
6710         var cfg = {
6711             cls: 'form-group' //input-group
6712         };
6713         
6714         
6715         var input =  {
6716             tag: 'input',
6717             id : id,
6718             type : this.inputType,
6719             cls : 'form-control',
6720             autocomplete: 'off',
6721             placeholder : this.placeholder || '' 
6722             
6723         };
6724         if (this.name) {
6725             input.name = this.name;
6726         }
6727         if (this.size) {
6728             input.cls += ' input-' + this.size;
6729         }
6730         
6731         if (this.disabled) {
6732             input.disabled=true;
6733         }
6734         
6735         var inputblock = input;
6736         
6737         if (this.before || this.after) {
6738             
6739             inputblock = {
6740                 cls : 'input-group',
6741                 cn :  [] 
6742             };
6743             if (this.before) {
6744                 inputblock.cn.push({
6745                     tag :'span',
6746                     cls : 'input-group-addon',
6747                     html : this.before
6748                 });
6749             }
6750             inputblock.cn.push(input);
6751             if (this.after) {
6752                 inputblock.cn.push({
6753                     tag :'span',
6754                     cls : 'input-group-addon',
6755                     html : this.after
6756                 });
6757             }
6758             
6759         };
6760         
6761         var box = {
6762             tag: 'div',
6763             cn: [
6764                 {
6765                     tag: 'input',
6766                     type : 'hidden',
6767                     cls: 'form-hidden-field'
6768                 },
6769                 inputblock
6770             ]
6771             
6772         };
6773         
6774         if(this.multiple){
6775             Roo.log('multiple');
6776             
6777             box = {
6778                 tag: 'div',
6779                 cn: [
6780                     {
6781                         tag: 'input',
6782                         type : 'hidden',
6783                         cls: 'form-hidden-field'
6784                     },
6785                     {
6786                         tag: 'ul',
6787                         cls: 'select2-choices',
6788                         cn:[
6789                             {
6790                                 tag: 'li',
6791                                 cls: 'select2-search-field',
6792                                 cn: [
6793
6794                                     inputblock
6795                                 ]
6796                             }
6797                         ]
6798                     }
6799                 ]
6800             }
6801         };
6802         
6803         var combobox = {
6804             cls: 'select2-container input-group',
6805             cn: [
6806                 box,
6807                 {
6808                     tag: 'ul',
6809                     cls: 'typeahead typeahead-long dropdown-menu',
6810                     style: 'display:none'
6811                 }
6812             ]
6813         };
6814         
6815         if(!this.multiple){
6816             combobox.cn.push({
6817                 tag :'span',
6818                 cls : 'input-group-addon btn dropdown-toggle',
6819                 cn : [
6820                     {
6821                         tag: 'span',
6822                         cls: 'caret'
6823                     },
6824                     {
6825                         tag: 'span',
6826                         cls: 'combobox-clear',
6827                         cn  : [
6828                             {
6829                                 tag : 'i',
6830                                 cls: 'icon-remove'
6831                             }
6832                         ]
6833                     }
6834                 ]
6835
6836             })
6837         }
6838         
6839         if(this.multiple){
6840             combobox.cls += ' select2-container-multi';
6841         }
6842         
6843         if (align ==='left' && this.fieldLabel.length) {
6844             
6845                 Roo.log("left and has label");
6846                 cfg.cn = [
6847                     
6848                     {
6849                         tag: 'label',
6850                         'for' :  id,
6851                         cls : 'control-label col-sm-' + this.labelWidth,
6852                         html : this.fieldLabel
6853                         
6854                     },
6855                     {
6856                         cls : "col-sm-" + (12 - this.labelWidth), 
6857                         cn: [
6858                             combobox
6859                         ]
6860                     }
6861                     
6862                 ];
6863         } else if ( this.fieldLabel.length) {
6864                 Roo.log(" label");
6865                  cfg.cn = [
6866                    
6867                     {
6868                         tag: 'label',
6869                         //cls : 'input-group-addon',
6870                         html : this.fieldLabel
6871                         
6872                     },
6873                     
6874                     combobox
6875                     
6876                 ];
6877
6878         } else {
6879             
6880                 Roo.log(" no label && no align");
6881                 cfg = combobox
6882                      
6883                 
6884         }
6885          
6886         var settings=this;
6887         ['xs','sm','md','lg'].map(function(size){
6888             if (settings[size]) {
6889                 cfg.cls += ' col-' + size + '-' + settings[size];
6890             }
6891         });
6892         
6893         return cfg;
6894         
6895     },
6896     
6897     
6898     
6899     // private
6900     onResize : function(w, h){
6901 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
6902 //        if(typeof w == 'number'){
6903 //            var x = w - this.trigger.getWidth();
6904 //            this.inputEl().setWidth(this.adjustWidth('input', x));
6905 //            this.trigger.setStyle('left', x+'px');
6906 //        }
6907     },
6908
6909     // private
6910     adjustSize : Roo.BoxComponent.prototype.adjustSize,
6911
6912     // private
6913     getResizeEl : function(){
6914         return this.inputEl();
6915     },
6916
6917     // private
6918     getPositionEl : function(){
6919         return this.inputEl();
6920     },
6921
6922     // private
6923     alignErrorIcon : function(){
6924         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
6925     },
6926
6927     // private
6928     initEvents : function(){
6929         
6930         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
6931         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
6932         if(!this.multiple){
6933             this.trigger = this.el.select('span.dropdown-toggle',true).first();
6934             if(this.hideTrigger){
6935                 this.trigger.setDisplayed(false);
6936             }
6937             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
6938         }
6939         
6940         if(this.multiple){
6941             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
6942         }
6943         
6944         //this.trigger.addClassOnOver('x-form-trigger-over');
6945         //this.trigger.addClassOnClick('x-form-trigger-click');
6946         
6947         //if(!this.width){
6948         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
6949         //}
6950     },
6951
6952     // private
6953     initTrigger : function(){
6954        
6955     },
6956
6957     // private
6958     onDestroy : function(){
6959         if(this.trigger){
6960             this.trigger.removeAllListeners();
6961           //  this.trigger.remove();
6962         }
6963         //if(this.wrap){
6964         //    this.wrap.remove();
6965         //}
6966         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
6967     },
6968
6969     // private
6970     onFocus : function(){
6971         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
6972         /*
6973         if(!this.mimicing){
6974             this.wrap.addClass('x-trigger-wrap-focus');
6975             this.mimicing = true;
6976             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
6977             if(this.monitorTab){
6978                 this.el.on("keydown", this.checkTab, this);
6979             }
6980         }
6981         */
6982     },
6983
6984     // private
6985     checkTab : function(e){
6986         if(e.getKey() == e.TAB){
6987             this.triggerBlur();
6988         }
6989     },
6990
6991     // private
6992     onBlur : function(){
6993         // do nothing
6994     },
6995
6996     // private
6997     mimicBlur : function(e, t){
6998         /*
6999         if(!this.wrap.contains(t) && this.validateBlur()){
7000             this.triggerBlur();
7001         }
7002         */
7003     },
7004
7005     // private
7006     triggerBlur : function(){
7007         this.mimicing = false;
7008         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
7009         if(this.monitorTab){
7010             this.el.un("keydown", this.checkTab, this);
7011         }
7012         //this.wrap.removeClass('x-trigger-wrap-focus');
7013         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
7014     },
7015
7016     // private
7017     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
7018     validateBlur : function(e, t){
7019         return true;
7020     },
7021
7022     // private
7023     onDisable : function(){
7024         this.inputEl().dom.disabled = true;
7025         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
7026         //if(this.wrap){
7027         //    this.wrap.addClass('x-item-disabled');
7028         //}
7029     },
7030
7031     // private
7032     onEnable : function(){
7033         this.inputEl().dom.disabled = false;
7034         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
7035         //if(this.wrap){
7036         //    this.el.removeClass('x-item-disabled');
7037         //}
7038     },
7039
7040     // private
7041     onShow : function(){
7042         var ae = this.getActionEl();
7043         
7044         if(ae){
7045             ae.dom.style.display = '';
7046             ae.dom.style.visibility = 'visible';
7047         }
7048     },
7049
7050     // private
7051     
7052     onHide : function(){
7053         var ae = this.getActionEl();
7054         ae.dom.style.display = 'none';
7055     },
7056
7057     /**
7058      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
7059      * by an implementing function.
7060      * @method
7061      * @param {EventObject} e
7062      */
7063     onTriggerClick : Roo.emptyFn
7064 });
7065  /*
7066  * Based on:
7067  * Ext JS Library 1.1.1
7068  * Copyright(c) 2006-2007, Ext JS, LLC.
7069  *
7070  * Originally Released Under LGPL - original licence link has changed is not relivant.
7071  *
7072  * Fork - LGPL
7073  * <script type="text/javascript">
7074  */
7075
7076
7077 /**
7078  * @class Roo.data.SortTypes
7079  * @singleton
7080  * Defines the default sorting (casting?) comparison functions used when sorting data.
7081  */
7082 Roo.data.SortTypes = {
7083     /**
7084      * Default sort that does nothing
7085      * @param {Mixed} s The value being converted
7086      * @return {Mixed} The comparison value
7087      */
7088     none : function(s){
7089         return s;
7090     },
7091     
7092     /**
7093      * The regular expression used to strip tags
7094      * @type {RegExp}
7095      * @property
7096      */
7097     stripTagsRE : /<\/?[^>]+>/gi,
7098     
7099     /**
7100      * Strips all HTML tags to sort on text only
7101      * @param {Mixed} s The value being converted
7102      * @return {String} The comparison value
7103      */
7104     asText : function(s){
7105         return String(s).replace(this.stripTagsRE, "");
7106     },
7107     
7108     /**
7109      * Strips all HTML tags to sort on text only - Case insensitive
7110      * @param {Mixed} s The value being converted
7111      * @return {String} The comparison value
7112      */
7113     asUCText : function(s){
7114         return String(s).toUpperCase().replace(this.stripTagsRE, "");
7115     },
7116     
7117     /**
7118      * Case insensitive string
7119      * @param {Mixed} s The value being converted
7120      * @return {String} The comparison value
7121      */
7122     asUCString : function(s) {
7123         return String(s).toUpperCase();
7124     },
7125     
7126     /**
7127      * Date sorting
7128      * @param {Mixed} s The value being converted
7129      * @return {Number} The comparison value
7130      */
7131     asDate : function(s) {
7132         if(!s){
7133             return 0;
7134         }
7135         if(s instanceof Date){
7136             return s.getTime();
7137         }
7138         return Date.parse(String(s));
7139     },
7140     
7141     /**
7142      * Float sorting
7143      * @param {Mixed} s The value being converted
7144      * @return {Float} The comparison value
7145      */
7146     asFloat : function(s) {
7147         var val = parseFloat(String(s).replace(/,/g, ""));
7148         if(isNaN(val)) val = 0;
7149         return val;
7150     },
7151     
7152     /**
7153      * Integer sorting
7154      * @param {Mixed} s The value being converted
7155      * @return {Number} The comparison value
7156      */
7157     asInt : function(s) {
7158         var val = parseInt(String(s).replace(/,/g, ""));
7159         if(isNaN(val)) val = 0;
7160         return val;
7161     }
7162 };/*
7163  * Based on:
7164  * Ext JS Library 1.1.1
7165  * Copyright(c) 2006-2007, Ext JS, LLC.
7166  *
7167  * Originally Released Under LGPL - original licence link has changed is not relivant.
7168  *
7169  * Fork - LGPL
7170  * <script type="text/javascript">
7171  */
7172
7173 /**
7174 * @class Roo.data.Record
7175  * Instances of this class encapsulate both record <em>definition</em> information, and record
7176  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
7177  * to access Records cached in an {@link Roo.data.Store} object.<br>
7178  * <p>
7179  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
7180  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
7181  * objects.<br>
7182  * <p>
7183  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
7184  * @constructor
7185  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
7186  * {@link #create}. The parameters are the same.
7187  * @param {Array} data An associative Array of data values keyed by the field name.
7188  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
7189  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
7190  * not specified an integer id is generated.
7191  */
7192 Roo.data.Record = function(data, id){
7193     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
7194     this.data = data;
7195 };
7196
7197 /**
7198  * Generate a constructor for a specific record layout.
7199  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
7200  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
7201  * Each field definition object may contain the following properties: <ul>
7202  * <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,
7203  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
7204  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
7205  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
7206  * is being used, then this is a string containing the javascript expression to reference the data relative to 
7207  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
7208  * to the data item relative to the record element. If the mapping expression is the same as the field name,
7209  * this may be omitted.</p></li>
7210  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
7211  * <ul><li>auto (Default, implies no conversion)</li>
7212  * <li>string</li>
7213  * <li>int</li>
7214  * <li>float</li>
7215  * <li>boolean</li>
7216  * <li>date</li></ul></p></li>
7217  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
7218  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
7219  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
7220  * by the Reader into an object that will be stored in the Record. It is passed the
7221  * following parameters:<ul>
7222  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
7223  * </ul></p></li>
7224  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
7225  * </ul>
7226  * <br>usage:<br><pre><code>
7227 var TopicRecord = Roo.data.Record.create(
7228     {name: 'title', mapping: 'topic_title'},
7229     {name: 'author', mapping: 'username'},
7230     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
7231     {name: 'lastPost', mapping: 'post_time', type: 'date'},
7232     {name: 'lastPoster', mapping: 'user2'},
7233     {name: 'excerpt', mapping: 'post_text'}
7234 );
7235
7236 var myNewRecord = new TopicRecord({
7237     title: 'Do my job please',
7238     author: 'noobie',
7239     totalPosts: 1,
7240     lastPost: new Date(),
7241     lastPoster: 'Animal',
7242     excerpt: 'No way dude!'
7243 });
7244 myStore.add(myNewRecord);
7245 </code></pre>
7246  * @method create
7247  * @static
7248  */
7249 Roo.data.Record.create = function(o){
7250     var f = function(){
7251         f.superclass.constructor.apply(this, arguments);
7252     };
7253     Roo.extend(f, Roo.data.Record);
7254     var p = f.prototype;
7255     p.fields = new Roo.util.MixedCollection(false, function(field){
7256         return field.name;
7257     });
7258     for(var i = 0, len = o.length; i < len; i++){
7259         p.fields.add(new Roo.data.Field(o[i]));
7260     }
7261     f.getField = function(name){
7262         return p.fields.get(name);  
7263     };
7264     return f;
7265 };
7266
7267 Roo.data.Record.AUTO_ID = 1000;
7268 Roo.data.Record.EDIT = 'edit';
7269 Roo.data.Record.REJECT = 'reject';
7270 Roo.data.Record.COMMIT = 'commit';
7271
7272 Roo.data.Record.prototype = {
7273     /**
7274      * Readonly flag - true if this record has been modified.
7275      * @type Boolean
7276      */
7277     dirty : false,
7278     editing : false,
7279     error: null,
7280     modified: null,
7281
7282     // private
7283     join : function(store){
7284         this.store = store;
7285     },
7286
7287     /**
7288      * Set the named field to the specified value.
7289      * @param {String} name The name of the field to set.
7290      * @param {Object} value The value to set the field to.
7291      */
7292     set : function(name, value){
7293         if(this.data[name] == value){
7294             return;
7295         }
7296         this.dirty = true;
7297         if(!this.modified){
7298             this.modified = {};
7299         }
7300         if(typeof this.modified[name] == 'undefined'){
7301             this.modified[name] = this.data[name];
7302         }
7303         this.data[name] = value;
7304         if(!this.editing && this.store){
7305             this.store.afterEdit(this);
7306         }       
7307     },
7308
7309     /**
7310      * Get the value of the named field.
7311      * @param {String} name The name of the field to get the value of.
7312      * @return {Object} The value of the field.
7313      */
7314     get : function(name){
7315         return this.data[name]; 
7316     },
7317
7318     // private
7319     beginEdit : function(){
7320         this.editing = true;
7321         this.modified = {}; 
7322     },
7323
7324     // private
7325     cancelEdit : function(){
7326         this.editing = false;
7327         delete this.modified;
7328     },
7329
7330     // private
7331     endEdit : function(){
7332         this.editing = false;
7333         if(this.dirty && this.store){
7334             this.store.afterEdit(this);
7335         }
7336     },
7337
7338     /**
7339      * Usually called by the {@link Roo.data.Store} which owns the Record.
7340      * Rejects all changes made to the Record since either creation, or the last commit operation.
7341      * Modified fields are reverted to their original values.
7342      * <p>
7343      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
7344      * of reject operations.
7345      */
7346     reject : function(){
7347         var m = this.modified;
7348         for(var n in m){
7349             if(typeof m[n] != "function"){
7350                 this.data[n] = m[n];
7351             }
7352         }
7353         this.dirty = false;
7354         delete this.modified;
7355         this.editing = false;
7356         if(this.store){
7357             this.store.afterReject(this);
7358         }
7359     },
7360
7361     /**
7362      * Usually called by the {@link Roo.data.Store} which owns the Record.
7363      * Commits all changes made to the Record since either creation, or the last commit operation.
7364      * <p>
7365      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
7366      * of commit operations.
7367      */
7368     commit : function(){
7369         this.dirty = false;
7370         delete this.modified;
7371         this.editing = false;
7372         if(this.store){
7373             this.store.afterCommit(this);
7374         }
7375     },
7376
7377     // private
7378     hasError : function(){
7379         return this.error != null;
7380     },
7381
7382     // private
7383     clearError : function(){
7384         this.error = null;
7385     },
7386
7387     /**
7388      * Creates a copy of this record.
7389      * @param {String} id (optional) A new record id if you don't want to use this record's id
7390      * @return {Record}
7391      */
7392     copy : function(newId) {
7393         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
7394     }
7395 };/*
7396  * Based on:
7397  * Ext JS Library 1.1.1
7398  * Copyright(c) 2006-2007, Ext JS, LLC.
7399  *
7400  * Originally Released Under LGPL - original licence link has changed is not relivant.
7401  *
7402  * Fork - LGPL
7403  * <script type="text/javascript">
7404  */
7405
7406
7407
7408 /**
7409  * @class Roo.data.Store
7410  * @extends Roo.util.Observable
7411  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
7412  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
7413  * <p>
7414  * 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
7415  * has no knowledge of the format of the data returned by the Proxy.<br>
7416  * <p>
7417  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
7418  * instances from the data object. These records are cached and made available through accessor functions.
7419  * @constructor
7420  * Creates a new Store.
7421  * @param {Object} config A config object containing the objects needed for the Store to access data,
7422  * and read the data into Records.
7423  */
7424 Roo.data.Store = function(config){
7425     this.data = new Roo.util.MixedCollection(false);
7426     this.data.getKey = function(o){
7427         return o.id;
7428     };
7429     this.baseParams = {};
7430     // private
7431     this.paramNames = {
7432         "start" : "start",
7433         "limit" : "limit",
7434         "sort" : "sort",
7435         "dir" : "dir",
7436         "multisort" : "_multisort"
7437     };
7438
7439     if(config && config.data){
7440         this.inlineData = config.data;
7441         delete config.data;
7442     }
7443
7444     Roo.apply(this, config);
7445     
7446     if(this.reader){ // reader passed
7447         this.reader = Roo.factory(this.reader, Roo.data);
7448         this.reader.xmodule = this.xmodule || false;
7449         if(!this.recordType){
7450             this.recordType = this.reader.recordType;
7451         }
7452         if(this.reader.onMetaChange){
7453             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
7454         }
7455     }
7456
7457     if(this.recordType){
7458         this.fields = this.recordType.prototype.fields;
7459     }
7460     this.modified = [];
7461
7462     this.addEvents({
7463         /**
7464          * @event datachanged
7465          * Fires when the data cache has changed, and a widget which is using this Store
7466          * as a Record cache should refresh its view.
7467          * @param {Store} this
7468          */
7469         datachanged : true,
7470         /**
7471          * @event metachange
7472          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
7473          * @param {Store} this
7474          * @param {Object} meta The JSON metadata
7475          */
7476         metachange : true,
7477         /**
7478          * @event add
7479          * Fires when Records have been added to the Store
7480          * @param {Store} this
7481          * @param {Roo.data.Record[]} records The array of Records added
7482          * @param {Number} index The index at which the record(s) were added
7483          */
7484         add : true,
7485         /**
7486          * @event remove
7487          * Fires when a Record has been removed from the Store
7488          * @param {Store} this
7489          * @param {Roo.data.Record} record The Record that was removed
7490          * @param {Number} index The index at which the record was removed
7491          */
7492         remove : true,
7493         /**
7494          * @event update
7495          * Fires when a Record has been updated
7496          * @param {Store} this
7497          * @param {Roo.data.Record} record The Record that was updated
7498          * @param {String} operation The update operation being performed.  Value may be one of:
7499          * <pre><code>
7500  Roo.data.Record.EDIT
7501  Roo.data.Record.REJECT
7502  Roo.data.Record.COMMIT
7503          * </code></pre>
7504          */
7505         update : true,
7506         /**
7507          * @event clear
7508          * Fires when the data cache has been cleared.
7509          * @param {Store} this
7510          */
7511         clear : true,
7512         /**
7513          * @event beforeload
7514          * Fires before a request is made for a new data object.  If the beforeload handler returns false
7515          * the load action will be canceled.
7516          * @param {Store} this
7517          * @param {Object} options The loading options that were specified (see {@link #load} for details)
7518          */
7519         beforeload : true,
7520         /**
7521          * @event beforeloadadd
7522          * Fires after a new set of Records has been loaded.
7523          * @param {Store} this
7524          * @param {Roo.data.Record[]} records The Records that were loaded
7525          * @param {Object} options The loading options that were specified (see {@link #load} for details)
7526          */
7527         beforeloadadd : true,
7528         /**
7529          * @event load
7530          * Fires after a new set of Records has been loaded, before they are added to the store.
7531          * @param {Store} this
7532          * @param {Roo.data.Record[]} records The Records that were loaded
7533          * @param {Object} options The loading options that were specified (see {@link #load} for details)
7534          * @params {Object} return from reader
7535          */
7536         load : true,
7537         /**
7538          * @event loadexception
7539          * Fires if an exception occurs in the Proxy during loading.
7540          * Called with the signature of the Proxy's "loadexception" event.
7541          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
7542          * 
7543          * @param {Proxy} 
7544          * @param {Object} return from JsonData.reader() - success, totalRecords, records
7545          * @param {Object} load options 
7546          * @param {Object} jsonData from your request (normally this contains the Exception)
7547          */
7548         loadexception : true
7549     });
7550     
7551     if(this.proxy){
7552         this.proxy = Roo.factory(this.proxy, Roo.data);
7553         this.proxy.xmodule = this.xmodule || false;
7554         this.relayEvents(this.proxy,  ["loadexception"]);
7555     }
7556     this.sortToggle = {};
7557     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
7558
7559     Roo.data.Store.superclass.constructor.call(this);
7560
7561     if(this.inlineData){
7562         this.loadData(this.inlineData);
7563         delete this.inlineData;
7564     }
7565 };
7566
7567 Roo.extend(Roo.data.Store, Roo.util.Observable, {
7568      /**
7569     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
7570     * without a remote query - used by combo/forms at present.
7571     */
7572     
7573     /**
7574     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
7575     */
7576     /**
7577     * @cfg {Array} data Inline data to be loaded when the store is initialized.
7578     */
7579     /**
7580     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
7581     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
7582     */
7583     /**
7584     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
7585     * on any HTTP request
7586     */
7587     /**
7588     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
7589     */
7590     /**
7591     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
7592     */
7593     multiSort: false,
7594     /**
7595     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
7596     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
7597     */
7598     remoteSort : false,
7599
7600     /**
7601     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
7602      * loaded or when a record is removed. (defaults to false).
7603     */
7604     pruneModifiedRecords : false,
7605
7606     // private
7607     lastOptions : null,
7608
7609     /**
7610      * Add Records to the Store and fires the add event.
7611      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
7612      */
7613     add : function(records){
7614         records = [].concat(records);
7615         for(var i = 0, len = records.length; i < len; i++){
7616             records[i].join(this);
7617         }
7618         var index = this.data.length;
7619         this.data.addAll(records);
7620         this.fireEvent("add", this, records, index);
7621     },
7622
7623     /**
7624      * Remove a Record from the Store and fires the remove event.
7625      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
7626      */
7627     remove : function(record){
7628         var index = this.data.indexOf(record);
7629         this.data.removeAt(index);
7630         if(this.pruneModifiedRecords){
7631             this.modified.remove(record);
7632         }
7633         this.fireEvent("remove", this, record, index);
7634     },
7635
7636     /**
7637      * Remove all Records from the Store and fires the clear event.
7638      */
7639     removeAll : function(){
7640         this.data.clear();
7641         if(this.pruneModifiedRecords){
7642             this.modified = [];
7643         }
7644         this.fireEvent("clear", this);
7645     },
7646
7647     /**
7648      * Inserts Records to the Store at the given index and fires the add event.
7649      * @param {Number} index The start index at which to insert the passed Records.
7650      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
7651      */
7652     insert : function(index, records){
7653         records = [].concat(records);
7654         for(var i = 0, len = records.length; i < len; i++){
7655             this.data.insert(index, records[i]);
7656             records[i].join(this);
7657         }
7658         this.fireEvent("add", this, records, index);
7659     },
7660
7661     /**
7662      * Get the index within the cache of the passed Record.
7663      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
7664      * @return {Number} The index of the passed Record. Returns -1 if not found.
7665      */
7666     indexOf : function(record){
7667         return this.data.indexOf(record);
7668     },
7669
7670     /**
7671      * Get the index within the cache of the Record with the passed id.
7672      * @param {String} id The id of the Record to find.
7673      * @return {Number} The index of the Record. Returns -1 if not found.
7674      */
7675     indexOfId : function(id){
7676         return this.data.indexOfKey(id);
7677     },
7678
7679     /**
7680      * Get the Record with the specified id.
7681      * @param {String} id The id of the Record to find.
7682      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
7683      */
7684     getById : function(id){
7685         return this.data.key(id);
7686     },
7687
7688     /**
7689      * Get the Record at the specified index.
7690      * @param {Number} index The index of the Record to find.
7691      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
7692      */
7693     getAt : function(index){
7694         return this.data.itemAt(index);
7695     },
7696
7697     /**
7698      * Returns a range of Records between specified indices.
7699      * @param {Number} startIndex (optional) The starting index (defaults to 0)
7700      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
7701      * @return {Roo.data.Record[]} An array of Records
7702      */
7703     getRange : function(start, end){
7704         return this.data.getRange(start, end);
7705     },
7706
7707     // private
7708     storeOptions : function(o){
7709         o = Roo.apply({}, o);
7710         delete o.callback;
7711         delete o.scope;
7712         this.lastOptions = o;
7713     },
7714
7715     /**
7716      * Loads the Record cache from the configured Proxy using the configured Reader.
7717      * <p>
7718      * If using remote paging, then the first load call must specify the <em>start</em>
7719      * and <em>limit</em> properties in the options.params property to establish the initial
7720      * position within the dataset, and the number of Records to cache on each read from the Proxy.
7721      * <p>
7722      * <strong>It is important to note that for remote data sources, loading is asynchronous,
7723      * and this call will return before the new data has been loaded. Perform any post-processing
7724      * in a callback function, or in a "load" event handler.</strong>
7725      * <p>
7726      * @param {Object} options An object containing properties which control loading options:<ul>
7727      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
7728      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
7729      * passed the following arguments:<ul>
7730      * <li>r : Roo.data.Record[]</li>
7731      * <li>options: Options object from the load call</li>
7732      * <li>success: Boolean success indicator</li></ul></li>
7733      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
7734      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
7735      * </ul>
7736      */
7737     load : function(options){
7738         options = options || {};
7739         if(this.fireEvent("beforeload", this, options) !== false){
7740             this.storeOptions(options);
7741             var p = Roo.apply(options.params || {}, this.baseParams);
7742             // if meta was not loaded from remote source.. try requesting it.
7743             if (!this.reader.metaFromRemote) {
7744                 p._requestMeta = 1;
7745             }
7746             if(this.sortInfo && this.remoteSort){
7747                 var pn = this.paramNames;
7748                 p[pn["sort"]] = this.sortInfo.field;
7749                 p[pn["dir"]] = this.sortInfo.direction;
7750             }
7751             if (this.multiSort) {
7752                 var pn = this.paramNames;
7753                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
7754             }
7755             
7756             this.proxy.load(p, this.reader, this.loadRecords, this, options);
7757         }
7758     },
7759
7760     /**
7761      * Reloads the Record cache from the configured Proxy using the configured Reader and
7762      * the options from the last load operation performed.
7763      * @param {Object} options (optional) An object containing properties which may override the options
7764      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
7765      * the most recently used options are reused).
7766      */
7767     reload : function(options){
7768         this.load(Roo.applyIf(options||{}, this.lastOptions));
7769     },
7770
7771     // private
7772     // Called as a callback by the Reader during a load operation.
7773     loadRecords : function(o, options, success){
7774         if(!o || success === false){
7775             if(success !== false){
7776                 this.fireEvent("load", this, [], options, o);
7777             }
7778             if(options.callback){
7779                 options.callback.call(options.scope || this, [], options, false);
7780             }
7781             return;
7782         }
7783         // if data returned failure - throw an exception.
7784         if (o.success === false) {
7785             // show a message if no listener is registered.
7786             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
7787                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
7788             }
7789             // loadmask wil be hooked into this..
7790             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
7791             return;
7792         }
7793         var r = o.records, t = o.totalRecords || r.length;
7794         
7795         this.fireEvent("beforeloadadd", this, r, options, o);
7796         
7797         if(!options || options.add !== true){
7798             if(this.pruneModifiedRecords){
7799                 this.modified = [];
7800             }
7801             for(var i = 0, len = r.length; i < len; i++){
7802                 r[i].join(this);
7803             }
7804             if(this.snapshot){
7805                 this.data = this.snapshot;
7806                 delete this.snapshot;
7807             }
7808             this.data.clear();
7809             this.data.addAll(r);
7810             this.totalLength = t;
7811             this.applySort();
7812             this.fireEvent("datachanged", this);
7813         }else{
7814             this.totalLength = Math.max(t, this.data.length+r.length);
7815             this.add(r);
7816         }
7817         this.fireEvent("load", this, r, options, o);
7818         if(options.callback){
7819             options.callback.call(options.scope || this, r, options, true);
7820         }
7821     },
7822
7823
7824     /**
7825      * Loads data from a passed data block. A Reader which understands the format of the data
7826      * must have been configured in the constructor.
7827      * @param {Object} data The data block from which to read the Records.  The format of the data expected
7828      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
7829      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
7830      */
7831     loadData : function(o, append){
7832         var r = this.reader.readRecords(o);
7833         this.loadRecords(r, {add: append}, true);
7834     },
7835
7836     /**
7837      * Gets the number of cached records.
7838      * <p>
7839      * <em>If using paging, this may not be the total size of the dataset. If the data object
7840      * used by the Reader contains the dataset size, then the getTotalCount() function returns
7841      * the data set size</em>
7842      */
7843     getCount : function(){
7844         return this.data.length || 0;
7845     },
7846
7847     /**
7848      * Gets the total number of records in the dataset as returned by the server.
7849      * <p>
7850      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
7851      * the dataset size</em>
7852      */
7853     getTotalCount : function(){
7854         return this.totalLength || 0;
7855     },
7856
7857     /**
7858      * Returns the sort state of the Store as an object with two properties:
7859      * <pre><code>
7860  field {String} The name of the field by which the Records are sorted
7861  direction {String} The sort order, "ASC" or "DESC"
7862      * </code></pre>
7863      */
7864     getSortState : function(){
7865         return this.sortInfo;
7866     },
7867
7868     // private
7869     applySort : function(){
7870         if(this.sortInfo && !this.remoteSort){
7871             var s = this.sortInfo, f = s.field;
7872             var st = this.fields.get(f).sortType;
7873             var fn = function(r1, r2){
7874                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
7875                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
7876             };
7877             this.data.sort(s.direction, fn);
7878             if(this.snapshot && this.snapshot != this.data){
7879                 this.snapshot.sort(s.direction, fn);
7880             }
7881         }
7882     },
7883
7884     /**
7885      * Sets the default sort column and order to be used by the next load operation.
7886      * @param {String} fieldName The name of the field to sort by.
7887      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
7888      */
7889     setDefaultSort : function(field, dir){
7890         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
7891     },
7892
7893     /**
7894      * Sort the Records.
7895      * If remote sorting is used, the sort is performed on the server, and the cache is
7896      * reloaded. If local sorting is used, the cache is sorted internally.
7897      * @param {String} fieldName The name of the field to sort by.
7898      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
7899      */
7900     sort : function(fieldName, dir){
7901         var f = this.fields.get(fieldName);
7902         if(!dir){
7903             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
7904             
7905             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
7906                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
7907             }else{
7908                 dir = f.sortDir;
7909             }
7910         }
7911         this.sortToggle[f.name] = dir;
7912         this.sortInfo = {field: f.name, direction: dir};
7913         if(!this.remoteSort){
7914             this.applySort();
7915             this.fireEvent("datachanged", this);
7916         }else{
7917             this.load(this.lastOptions);
7918         }
7919     },
7920
7921     /**
7922      * Calls the specified function for each of the Records in the cache.
7923      * @param {Function} fn The function to call. The Record is passed as the first parameter.
7924      * Returning <em>false</em> aborts and exits the iteration.
7925      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
7926      */
7927     each : function(fn, scope){
7928         this.data.each(fn, scope);
7929     },
7930
7931     /**
7932      * Gets all records modified since the last commit.  Modified records are persisted across load operations
7933      * (e.g., during paging).
7934      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
7935      */
7936     getModifiedRecords : function(){
7937         return this.modified;
7938     },
7939
7940     // private
7941     createFilterFn : function(property, value, anyMatch){
7942         if(!value.exec){ // not a regex
7943             value = String(value);
7944             if(value.length == 0){
7945                 return false;
7946             }
7947             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
7948         }
7949         return function(r){
7950             return value.test(r.data[property]);
7951         };
7952     },
7953
7954     /**
7955      * Sums the value of <i>property</i> for each record between start and end and returns the result.
7956      * @param {String} property A field on your records
7957      * @param {Number} start The record index to start at (defaults to 0)
7958      * @param {Number} end The last record index to include (defaults to length - 1)
7959      * @return {Number} The sum
7960      */
7961     sum : function(property, start, end){
7962         var rs = this.data.items, v = 0;
7963         start = start || 0;
7964         end = (end || end === 0) ? end : rs.length-1;
7965
7966         for(var i = start; i <= end; i++){
7967             v += (rs[i].data[property] || 0);
7968         }
7969         return v;
7970     },
7971
7972     /**
7973      * Filter the records by a specified property.
7974      * @param {String} field A field on your records
7975      * @param {String/RegExp} value Either a string that the field
7976      * should start with or a RegExp to test against the field
7977      * @param {Boolean} anyMatch True to match any part not just the beginning
7978      */
7979     filter : function(property, value, anyMatch){
7980         var fn = this.createFilterFn(property, value, anyMatch);
7981         return fn ? this.filterBy(fn) : this.clearFilter();
7982     },
7983
7984     /**
7985      * Filter by a function. The specified function will be called with each
7986      * record in this data source. If the function returns true the record is included,
7987      * otherwise it is filtered.
7988      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
7989      * @param {Object} scope (optional) The scope of the function (defaults to this)
7990      */
7991     filterBy : function(fn, scope){
7992         this.snapshot = this.snapshot || this.data;
7993         this.data = this.queryBy(fn, scope||this);
7994         this.fireEvent("datachanged", this);
7995     },
7996
7997     /**
7998      * Query the records by a specified property.
7999      * @param {String} field A field on your records
8000      * @param {String/RegExp} value Either a string that the field
8001      * should start with or a RegExp to test against the field
8002      * @param {Boolean} anyMatch True to match any part not just the beginning
8003      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
8004      */
8005     query : function(property, value, anyMatch){
8006         var fn = this.createFilterFn(property, value, anyMatch);
8007         return fn ? this.queryBy(fn) : this.data.clone();
8008     },
8009
8010     /**
8011      * Query by a function. The specified function will be called with each
8012      * record in this data source. If the function returns true the record is included
8013      * in the results.
8014      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
8015      * @param {Object} scope (optional) The scope of the function (defaults to this)
8016       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
8017      **/
8018     queryBy : function(fn, scope){
8019         var data = this.snapshot || this.data;
8020         return data.filterBy(fn, scope||this);
8021     },
8022
8023     /**
8024      * Collects unique values for a particular dataIndex from this store.
8025      * @param {String} dataIndex The property to collect
8026      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
8027      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
8028      * @return {Array} An array of the unique values
8029      **/
8030     collect : function(dataIndex, allowNull, bypassFilter){
8031         var d = (bypassFilter === true && this.snapshot) ?
8032                 this.snapshot.items : this.data.items;
8033         var v, sv, r = [], l = {};
8034         for(var i = 0, len = d.length; i < len; i++){
8035             v = d[i].data[dataIndex];
8036             sv = String(v);
8037             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
8038                 l[sv] = true;
8039                 r[r.length] = v;
8040             }
8041         }
8042         return r;
8043     },
8044
8045     /**
8046      * Revert to a view of the Record cache with no filtering applied.
8047      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
8048      */
8049     clearFilter : function(suppressEvent){
8050         if(this.snapshot && this.snapshot != this.data){
8051             this.data = this.snapshot;
8052             delete this.snapshot;
8053             if(suppressEvent !== true){
8054                 this.fireEvent("datachanged", this);
8055             }
8056         }
8057     },
8058
8059     // private
8060     afterEdit : function(record){
8061         if(this.modified.indexOf(record) == -1){
8062             this.modified.push(record);
8063         }
8064         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
8065     },
8066     
8067     // private
8068     afterReject : function(record){
8069         this.modified.remove(record);
8070         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
8071     },
8072
8073     // private
8074     afterCommit : function(record){
8075         this.modified.remove(record);
8076         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
8077     },
8078
8079     /**
8080      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
8081      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
8082      */
8083     commitChanges : function(){
8084         var m = this.modified.slice(0);
8085         this.modified = [];
8086         for(var i = 0, len = m.length; i < len; i++){
8087             m[i].commit();
8088         }
8089     },
8090
8091     /**
8092      * Cancel outstanding changes on all changed records.
8093      */
8094     rejectChanges : function(){
8095         var m = this.modified.slice(0);
8096         this.modified = [];
8097         for(var i = 0, len = m.length; i < len; i++){
8098             m[i].reject();
8099         }
8100     },
8101
8102     onMetaChange : function(meta, rtype, o){
8103         this.recordType = rtype;
8104         this.fields = rtype.prototype.fields;
8105         delete this.snapshot;
8106         this.sortInfo = meta.sortInfo || this.sortInfo;
8107         this.modified = [];
8108         this.fireEvent('metachange', this, this.reader.meta);
8109     },
8110     
8111     moveIndex : function(data, type)
8112     {
8113         var index = this.indexOf(data);
8114         
8115         var newIndex = index + type;
8116         
8117         this.remove(data);
8118         
8119         this.insert(newIndex, data);
8120         
8121     }
8122 });/*
8123  * Based on:
8124  * Ext JS Library 1.1.1
8125  * Copyright(c) 2006-2007, Ext JS, LLC.
8126  *
8127  * Originally Released Under LGPL - original licence link has changed is not relivant.
8128  *
8129  * Fork - LGPL
8130  * <script type="text/javascript">
8131  */
8132
8133 /**
8134  * @class Roo.data.SimpleStore
8135  * @extends Roo.data.Store
8136  * Small helper class to make creating Stores from Array data easier.
8137  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
8138  * @cfg {Array} fields An array of field definition objects, or field name strings.
8139  * @cfg {Array} data The multi-dimensional array of data
8140  * @constructor
8141  * @param {Object} config
8142  */
8143 Roo.data.SimpleStore = function(config){
8144     Roo.data.SimpleStore.superclass.constructor.call(this, {
8145         isLocal : true,
8146         reader: new Roo.data.ArrayReader({
8147                 id: config.id
8148             },
8149             Roo.data.Record.create(config.fields)
8150         ),
8151         proxy : new Roo.data.MemoryProxy(config.data)
8152     });
8153     this.load();
8154 };
8155 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
8156  * Based on:
8157  * Ext JS Library 1.1.1
8158  * Copyright(c) 2006-2007, Ext JS, LLC.
8159  *
8160  * Originally Released Under LGPL - original licence link has changed is not relivant.
8161  *
8162  * Fork - LGPL
8163  * <script type="text/javascript">
8164  */
8165
8166 /**
8167 /**
8168  * @extends Roo.data.Store
8169  * @class Roo.data.JsonStore
8170  * Small helper class to make creating Stores for JSON data easier. <br/>
8171 <pre><code>
8172 var store = new Roo.data.JsonStore({
8173     url: 'get-images.php',
8174     root: 'images',
8175     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
8176 });
8177 </code></pre>
8178  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
8179  * JsonReader and HttpProxy (unless inline data is provided).</b>
8180  * @cfg {Array} fields An array of field definition objects, or field name strings.
8181  * @constructor
8182  * @param {Object} config
8183  */
8184 Roo.data.JsonStore = function(c){
8185     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
8186         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
8187         reader: new Roo.data.JsonReader(c, c.fields)
8188     }));
8189 };
8190 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
8191  * Based on:
8192  * Ext JS Library 1.1.1
8193  * Copyright(c) 2006-2007, Ext JS, LLC.
8194  *
8195  * Originally Released Under LGPL - original licence link has changed is not relivant.
8196  *
8197  * Fork - LGPL
8198  * <script type="text/javascript">
8199  */
8200
8201  
8202 Roo.data.Field = function(config){
8203     if(typeof config == "string"){
8204         config = {name: config};
8205     }
8206     Roo.apply(this, config);
8207     
8208     if(!this.type){
8209         this.type = "auto";
8210     }
8211     
8212     var st = Roo.data.SortTypes;
8213     // named sortTypes are supported, here we look them up
8214     if(typeof this.sortType == "string"){
8215         this.sortType = st[this.sortType];
8216     }
8217     
8218     // set default sortType for strings and dates
8219     if(!this.sortType){
8220         switch(this.type){
8221             case "string":
8222                 this.sortType = st.asUCString;
8223                 break;
8224             case "date":
8225                 this.sortType = st.asDate;
8226                 break;
8227             default:
8228                 this.sortType = st.none;
8229         }
8230     }
8231
8232     // define once
8233     var stripRe = /[\$,%]/g;
8234
8235     // prebuilt conversion function for this field, instead of
8236     // switching every time we're reading a value
8237     if(!this.convert){
8238         var cv, dateFormat = this.dateFormat;
8239         switch(this.type){
8240             case "":
8241             case "auto":
8242             case undefined:
8243                 cv = function(v){ return v; };
8244                 break;
8245             case "string":
8246                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
8247                 break;
8248             case "int":
8249                 cv = function(v){
8250                     return v !== undefined && v !== null && v !== '' ?
8251                            parseInt(String(v).replace(stripRe, ""), 10) : '';
8252                     };
8253                 break;
8254             case "float":
8255                 cv = function(v){
8256                     return v !== undefined && v !== null && v !== '' ?
8257                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
8258                     };
8259                 break;
8260             case "bool":
8261             case "boolean":
8262                 cv = function(v){ return v === true || v === "true" || v == 1; };
8263                 break;
8264             case "date":
8265                 cv = function(v){
8266                     if(!v){
8267                         return '';
8268                     }
8269                     if(v instanceof Date){
8270                         return v;
8271                     }
8272                     if(dateFormat){
8273                         if(dateFormat == "timestamp"){
8274                             return new Date(v*1000);
8275                         }
8276                         return Date.parseDate(v, dateFormat);
8277                     }
8278                     var parsed = Date.parse(v);
8279                     return parsed ? new Date(parsed) : null;
8280                 };
8281              break;
8282             
8283         }
8284         this.convert = cv;
8285     }
8286 };
8287
8288 Roo.data.Field.prototype = {
8289     dateFormat: null,
8290     defaultValue: "",
8291     mapping: null,
8292     sortType : null,
8293     sortDir : "ASC"
8294 };/*
8295  * Based on:
8296  * Ext JS Library 1.1.1
8297  * Copyright(c) 2006-2007, Ext JS, LLC.
8298  *
8299  * Originally Released Under LGPL - original licence link has changed is not relivant.
8300  *
8301  * Fork - LGPL
8302  * <script type="text/javascript">
8303  */
8304  
8305 // Base class for reading structured data from a data source.  This class is intended to be
8306 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
8307
8308 /**
8309  * @class Roo.data.DataReader
8310  * Base class for reading structured data from a data source.  This class is intended to be
8311  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
8312  */
8313
8314 Roo.data.DataReader = function(meta, recordType){
8315     
8316     this.meta = meta;
8317     
8318     this.recordType = recordType instanceof Array ? 
8319         Roo.data.Record.create(recordType) : recordType;
8320 };
8321
8322 Roo.data.DataReader.prototype = {
8323      /**
8324      * Create an empty record
8325      * @param {Object} data (optional) - overlay some values
8326      * @return {Roo.data.Record} record created.
8327      */
8328     newRow :  function(d) {
8329         var da =  {};
8330         this.recordType.prototype.fields.each(function(c) {
8331             switch( c.type) {
8332                 case 'int' : da[c.name] = 0; break;
8333                 case 'date' : da[c.name] = new Date(); break;
8334                 case 'float' : da[c.name] = 0.0; break;
8335                 case 'boolean' : da[c.name] = false; break;
8336                 default : da[c.name] = ""; break;
8337             }
8338             
8339         });
8340         return new this.recordType(Roo.apply(da, d));
8341     }
8342     
8343 };/*
8344  * Based on:
8345  * Ext JS Library 1.1.1
8346  * Copyright(c) 2006-2007, Ext JS, LLC.
8347  *
8348  * Originally Released Under LGPL - original licence link has changed is not relivant.
8349  *
8350  * Fork - LGPL
8351  * <script type="text/javascript">
8352  */
8353
8354 /**
8355  * @class Roo.data.DataProxy
8356  * @extends Roo.data.Observable
8357  * This class is an abstract base class for implementations which provide retrieval of
8358  * unformatted data objects.<br>
8359  * <p>
8360  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
8361  * (of the appropriate type which knows how to parse the data object) to provide a block of
8362  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
8363  * <p>
8364  * Custom implementations must implement the load method as described in
8365  * {@link Roo.data.HttpProxy#load}.
8366  */
8367 Roo.data.DataProxy = function(){
8368     this.addEvents({
8369         /**
8370          * @event beforeload
8371          * Fires before a network request is made to retrieve a data object.
8372          * @param {Object} This DataProxy object.
8373          * @param {Object} params The params parameter to the load function.
8374          */
8375         beforeload : true,
8376         /**
8377          * @event load
8378          * Fires before the load method's callback is called.
8379          * @param {Object} This DataProxy object.
8380          * @param {Object} o The data object.
8381          * @param {Object} arg The callback argument object passed to the load function.
8382          */
8383         load : true,
8384         /**
8385          * @event loadexception
8386          * Fires if an Exception occurs during data retrieval.
8387          * @param {Object} This DataProxy object.
8388          * @param {Object} o The data object.
8389          * @param {Object} arg The callback argument object passed to the load function.
8390          * @param {Object} e The Exception.
8391          */
8392         loadexception : true
8393     });
8394     Roo.data.DataProxy.superclass.constructor.call(this);
8395 };
8396
8397 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
8398
8399     /**
8400      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
8401      */
8402 /*
8403  * Based on:
8404  * Ext JS Library 1.1.1
8405  * Copyright(c) 2006-2007, Ext JS, LLC.
8406  *
8407  * Originally Released Under LGPL - original licence link has changed is not relivant.
8408  *
8409  * Fork - LGPL
8410  * <script type="text/javascript">
8411  */
8412 /**
8413  * @class Roo.data.MemoryProxy
8414  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
8415  * to the Reader when its load method is called.
8416  * @constructor
8417  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
8418  */
8419 Roo.data.MemoryProxy = function(data){
8420     if (data.data) {
8421         data = data.data;
8422     }
8423     Roo.data.MemoryProxy.superclass.constructor.call(this);
8424     this.data = data;
8425 };
8426
8427 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
8428     /**
8429      * Load data from the requested source (in this case an in-memory
8430      * data object passed to the constructor), read the data object into
8431      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
8432      * process that block using the passed callback.
8433      * @param {Object} params This parameter is not used by the MemoryProxy class.
8434      * @param {Roo.data.DataReader} reader The Reader object which converts the data
8435      * object into a block of Roo.data.Records.
8436      * @param {Function} callback The function into which to pass the block of Roo.data.records.
8437      * The function must be passed <ul>
8438      * <li>The Record block object</li>
8439      * <li>The "arg" argument from the load function</li>
8440      * <li>A boolean success indicator</li>
8441      * </ul>
8442      * @param {Object} scope The scope in which to call the callback
8443      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
8444      */
8445     load : function(params, reader, callback, scope, arg){
8446         params = params || {};
8447         var result;
8448         try {
8449             result = reader.readRecords(this.data);
8450         }catch(e){
8451             this.fireEvent("loadexception", this, arg, null, e);
8452             callback.call(scope, null, arg, false);
8453             return;
8454         }
8455         callback.call(scope, result, arg, true);
8456     },
8457     
8458     // private
8459     update : function(params, records){
8460         
8461     }
8462 });/*
8463  * Based on:
8464  * Ext JS Library 1.1.1
8465  * Copyright(c) 2006-2007, Ext JS, LLC.
8466  *
8467  * Originally Released Under LGPL - original licence link has changed is not relivant.
8468  *
8469  * Fork - LGPL
8470  * <script type="text/javascript">
8471  */
8472 /**
8473  * @class Roo.data.HttpProxy
8474  * @extends Roo.data.DataProxy
8475  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
8476  * configured to reference a certain URL.<br><br>
8477  * <p>
8478  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
8479  * from which the running page was served.<br><br>
8480  * <p>
8481  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
8482  * <p>
8483  * Be aware that to enable the browser to parse an XML document, the server must set
8484  * the Content-Type header in the HTTP response to "text/xml".
8485  * @constructor
8486  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
8487  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
8488  * will be used to make the request.
8489  */
8490 Roo.data.HttpProxy = function(conn){
8491     Roo.data.HttpProxy.superclass.constructor.call(this);
8492     // is conn a conn config or a real conn?
8493     this.conn = conn;
8494     this.useAjax = !conn || !conn.events;
8495   
8496 };
8497
8498 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
8499     // thse are take from connection...
8500     
8501     /**
8502      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
8503      */
8504     /**
8505      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
8506      * extra parameters to each request made by this object. (defaults to undefined)
8507      */
8508     /**
8509      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
8510      *  to each request made by this object. (defaults to undefined)
8511      */
8512     /**
8513      * @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)
8514      */
8515     /**
8516      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
8517      */
8518      /**
8519      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
8520      * @type Boolean
8521      */
8522   
8523
8524     /**
8525      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
8526      * @type Boolean
8527      */
8528     /**
8529      * Return the {@link Roo.data.Connection} object being used by this Proxy.
8530      * @return {Connection} The Connection object. This object may be used to subscribe to events on
8531      * a finer-grained basis than the DataProxy events.
8532      */
8533     getConnection : function(){
8534         return this.useAjax ? Roo.Ajax : this.conn;
8535     },
8536
8537     /**
8538      * Load data from the configured {@link Roo.data.Connection}, read the data object into
8539      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
8540      * process that block using the passed callback.
8541      * @param {Object} params An object containing properties which are to be used as HTTP parameters
8542      * for the request to the remote server.
8543      * @param {Roo.data.DataReader} reader The Reader object which converts the data
8544      * object into a block of Roo.data.Records.
8545      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
8546      * The function must be passed <ul>
8547      * <li>The Record block object</li>
8548      * <li>The "arg" argument from the load function</li>
8549      * <li>A boolean success indicator</li>
8550      * </ul>
8551      * @param {Object} scope The scope in which to call the callback
8552      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
8553      */
8554     load : function(params, reader, callback, scope, arg){
8555         if(this.fireEvent("beforeload", this, params) !== false){
8556             var  o = {
8557                 params : params || {},
8558                 request: {
8559                     callback : callback,
8560                     scope : scope,
8561                     arg : arg
8562                 },
8563                 reader: reader,
8564                 callback : this.loadResponse,
8565                 scope: this
8566             };
8567             if(this.useAjax){
8568                 Roo.applyIf(o, this.conn);
8569                 if(this.activeRequest){
8570                     Roo.Ajax.abort(this.activeRequest);
8571                 }
8572                 this.activeRequest = Roo.Ajax.request(o);
8573             }else{
8574                 this.conn.request(o);
8575             }
8576         }else{
8577             callback.call(scope||this, null, arg, false);
8578         }
8579     },
8580
8581     // private
8582     loadResponse : function(o, success, response){
8583         delete this.activeRequest;
8584         if(!success){
8585             this.fireEvent("loadexception", this, o, response);
8586             o.request.callback.call(o.request.scope, null, o.request.arg, false);
8587             return;
8588         }
8589         var result;
8590         try {
8591             result = o.reader.read(response);
8592         }catch(e){
8593             this.fireEvent("loadexception", this, o, response, e);
8594             o.request.callback.call(o.request.scope, null, o.request.arg, false);
8595             return;
8596         }
8597         
8598         this.fireEvent("load", this, o, o.request.arg);
8599         o.request.callback.call(o.request.scope, result, o.request.arg, true);
8600     },
8601
8602     // private
8603     update : function(dataSet){
8604
8605     },
8606
8607     // private
8608     updateResponse : function(dataSet){
8609
8610     }
8611 });/*
8612  * Based on:
8613  * Ext JS Library 1.1.1
8614  * Copyright(c) 2006-2007, Ext JS, LLC.
8615  *
8616  * Originally Released Under LGPL - original licence link has changed is not relivant.
8617  *
8618  * Fork - LGPL
8619  * <script type="text/javascript">
8620  */
8621
8622 /**
8623  * @class Roo.data.ScriptTagProxy
8624  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
8625  * other than the originating domain of the running page.<br><br>
8626  * <p>
8627  * <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
8628  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
8629  * <p>
8630  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
8631  * source code that is used as the source inside a &lt;script> tag.<br><br>
8632  * <p>
8633  * In order for the browser to process the returned data, the server must wrap the data object
8634  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
8635  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
8636  * depending on whether the callback name was passed:
8637  * <p>
8638  * <pre><code>
8639 boolean scriptTag = false;
8640 String cb = request.getParameter("callback");
8641 if (cb != null) {
8642     scriptTag = true;
8643     response.setContentType("text/javascript");
8644 } else {
8645     response.setContentType("application/x-json");
8646 }
8647 Writer out = response.getWriter();
8648 if (scriptTag) {
8649     out.write(cb + "(");
8650 }
8651 out.print(dataBlock.toJsonString());
8652 if (scriptTag) {
8653     out.write(");");
8654 }
8655 </pre></code>
8656  *
8657  * @constructor
8658  * @param {Object} config A configuration object.
8659  */
8660 Roo.data.ScriptTagProxy = function(config){
8661     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
8662     Roo.apply(this, config);
8663     this.head = document.getElementsByTagName("head")[0];
8664 };
8665
8666 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
8667
8668 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
8669     /**
8670      * @cfg {String} url The URL from which to request the data object.
8671      */
8672     /**
8673      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
8674      */
8675     timeout : 30000,
8676     /**
8677      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
8678      * the server the name of the callback function set up by the load call to process the returned data object.
8679      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
8680      * javascript output which calls this named function passing the data object as its only parameter.
8681      */
8682     callbackParam : "callback",
8683     /**
8684      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
8685      * name to the request.
8686      */
8687     nocache : true,
8688
8689     /**
8690      * Load data from the configured URL, read the data object into
8691      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
8692      * process that block using the passed callback.
8693      * @param {Object} params An object containing properties which are to be used as HTTP parameters
8694      * for the request to the remote server.
8695      * @param {Roo.data.DataReader} reader The Reader object which converts the data
8696      * object into a block of Roo.data.Records.
8697      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
8698      * The function must be passed <ul>
8699      * <li>The Record block object</li>
8700      * <li>The "arg" argument from the load function</li>
8701      * <li>A boolean success indicator</li>
8702      * </ul>
8703      * @param {Object} scope The scope in which to call the callback
8704      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
8705      */
8706     load : function(params, reader, callback, scope, arg){
8707         if(this.fireEvent("beforeload", this, params) !== false){
8708
8709             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
8710
8711             var url = this.url;
8712             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
8713             if(this.nocache){
8714                 url += "&_dc=" + (new Date().getTime());
8715             }
8716             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
8717             var trans = {
8718                 id : transId,
8719                 cb : "stcCallback"+transId,
8720                 scriptId : "stcScript"+transId,
8721                 params : params,
8722                 arg : arg,
8723                 url : url,
8724                 callback : callback,
8725                 scope : scope,
8726                 reader : reader
8727             };
8728             var conn = this;
8729
8730             window[trans.cb] = function(o){
8731                 conn.handleResponse(o, trans);
8732             };
8733
8734             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
8735
8736             if(this.autoAbort !== false){
8737                 this.abort();
8738             }
8739
8740             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
8741
8742             var script = document.createElement("script");
8743             script.setAttribute("src", url);
8744             script.setAttribute("type", "text/javascript");
8745             script.setAttribute("id", trans.scriptId);
8746             this.head.appendChild(script);
8747
8748             this.trans = trans;
8749         }else{
8750             callback.call(scope||this, null, arg, false);
8751         }
8752     },
8753
8754     // private
8755     isLoading : function(){
8756         return this.trans ? true : false;
8757     },
8758
8759     /**
8760      * Abort the current server request.
8761      */
8762     abort : function(){
8763         if(this.isLoading()){
8764             this.destroyTrans(this.trans);
8765         }
8766     },
8767
8768     // private
8769     destroyTrans : function(trans, isLoaded){
8770         this.head.removeChild(document.getElementById(trans.scriptId));
8771         clearTimeout(trans.timeoutId);
8772         if(isLoaded){
8773             window[trans.cb] = undefined;
8774             try{
8775                 delete window[trans.cb];
8776             }catch(e){}
8777         }else{
8778             // if hasn't been loaded, wait for load to remove it to prevent script error
8779             window[trans.cb] = function(){
8780                 window[trans.cb] = undefined;
8781                 try{
8782                     delete window[trans.cb];
8783                 }catch(e){}
8784             };
8785         }
8786     },
8787
8788     // private
8789     handleResponse : function(o, trans){
8790         this.trans = false;
8791         this.destroyTrans(trans, true);
8792         var result;
8793         try {
8794             result = trans.reader.readRecords(o);
8795         }catch(e){
8796             this.fireEvent("loadexception", this, o, trans.arg, e);
8797             trans.callback.call(trans.scope||window, null, trans.arg, false);
8798             return;
8799         }
8800         this.fireEvent("load", this, o, trans.arg);
8801         trans.callback.call(trans.scope||window, result, trans.arg, true);
8802     },
8803
8804     // private
8805     handleFailure : function(trans){
8806         this.trans = false;
8807         this.destroyTrans(trans, false);
8808         this.fireEvent("loadexception", this, null, trans.arg);
8809         trans.callback.call(trans.scope||window, null, trans.arg, false);
8810     }
8811 });/*
8812  * Based on:
8813  * Ext JS Library 1.1.1
8814  * Copyright(c) 2006-2007, Ext JS, LLC.
8815  *
8816  * Originally Released Under LGPL - original licence link has changed is not relivant.
8817  *
8818  * Fork - LGPL
8819  * <script type="text/javascript">
8820  */
8821
8822 /**
8823  * @class Roo.data.JsonReader
8824  * @extends Roo.data.DataReader
8825  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
8826  * based on mappings in a provided Roo.data.Record constructor.
8827  * 
8828  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
8829  * in the reply previously. 
8830  * 
8831  * <p>
8832  * Example code:
8833  * <pre><code>
8834 var RecordDef = Roo.data.Record.create([
8835     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
8836     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
8837 ]);
8838 var myReader = new Roo.data.JsonReader({
8839     totalProperty: "results",    // The property which contains the total dataset size (optional)
8840     root: "rows",                // The property which contains an Array of row objects
8841     id: "id"                     // The property within each row object that provides an ID for the record (optional)
8842 }, RecordDef);
8843 </code></pre>
8844  * <p>
8845  * This would consume a JSON file like this:
8846  * <pre><code>
8847 { 'results': 2, 'rows': [
8848     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
8849     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
8850 }
8851 </code></pre>
8852  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
8853  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
8854  * paged from the remote server.
8855  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
8856  * @cfg {String} root name of the property which contains the Array of row objects.
8857  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
8858  * @constructor
8859  * Create a new JsonReader
8860  * @param {Object} meta Metadata configuration options
8861  * @param {Object} recordType Either an Array of field definition objects,
8862  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
8863  */
8864 Roo.data.JsonReader = function(meta, recordType){
8865     
8866     meta = meta || {};
8867     // set some defaults:
8868     Roo.applyIf(meta, {
8869         totalProperty: 'total',
8870         successProperty : 'success',
8871         root : 'data',
8872         id : 'id'
8873     });
8874     
8875     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
8876 };
8877 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
8878     
8879     /**
8880      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
8881      * Used by Store query builder to append _requestMeta to params.
8882      * 
8883      */
8884     metaFromRemote : false,
8885     /**
8886      * This method is only used by a DataProxy which has retrieved data from a remote server.
8887      * @param {Object} response The XHR object which contains the JSON data in its responseText.
8888      * @return {Object} data A data block which is used by an Roo.data.Store object as
8889      * a cache of Roo.data.Records.
8890      */
8891     read : function(response){
8892         var json = response.responseText;
8893        
8894         var o = /* eval:var:o */ eval("("+json+")");
8895         if(!o) {
8896             throw {message: "JsonReader.read: Json object not found"};
8897         }
8898         
8899         if(o.metaData){
8900             
8901             delete this.ef;
8902             this.metaFromRemote = true;
8903             this.meta = o.metaData;
8904             this.recordType = Roo.data.Record.create(o.metaData.fields);
8905             this.onMetaChange(this.meta, this.recordType, o);
8906         }
8907         return this.readRecords(o);
8908     },
8909
8910     // private function a store will implement
8911     onMetaChange : function(meta, recordType, o){
8912
8913     },
8914
8915     /**
8916          * @ignore
8917          */
8918     simpleAccess: function(obj, subsc) {
8919         return obj[subsc];
8920     },
8921
8922         /**
8923          * @ignore
8924          */
8925     getJsonAccessor: function(){
8926         var re = /[\[\.]/;
8927         return function(expr) {
8928             try {
8929                 return(re.test(expr))
8930                     ? new Function("obj", "return obj." + expr)
8931                     : function(obj){
8932                         return obj[expr];
8933                     };
8934             } catch(e){}
8935             return Roo.emptyFn;
8936         };
8937     }(),
8938
8939     /**
8940      * Create a data block containing Roo.data.Records from an XML document.
8941      * @param {Object} o An object which contains an Array of row objects in the property specified
8942      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
8943      * which contains the total size of the dataset.
8944      * @return {Object} data A data block which is used by an Roo.data.Store object as
8945      * a cache of Roo.data.Records.
8946      */
8947     readRecords : function(o){
8948         /**
8949          * After any data loads, the raw JSON data is available for further custom processing.
8950          * @type Object
8951          */
8952         this.o = o;
8953         var s = this.meta, Record = this.recordType,
8954             f = Record.prototype.fields, fi = f.items, fl = f.length;
8955
8956 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
8957         if (!this.ef) {
8958             if(s.totalProperty) {
8959                     this.getTotal = this.getJsonAccessor(s.totalProperty);
8960                 }
8961                 if(s.successProperty) {
8962                     this.getSuccess = this.getJsonAccessor(s.successProperty);
8963                 }
8964                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
8965                 if (s.id) {
8966                         var g = this.getJsonAccessor(s.id);
8967                         this.getId = function(rec) {
8968                                 var r = g(rec);
8969                                 return (r === undefined || r === "") ? null : r;
8970                         };
8971                 } else {
8972                         this.getId = function(){return null;};
8973                 }
8974             this.ef = [];
8975             for(var jj = 0; jj < fl; jj++){
8976                 f = fi[jj];
8977                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
8978                 this.ef[jj] = this.getJsonAccessor(map);
8979             }
8980         }
8981
8982         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
8983         if(s.totalProperty){
8984             var vt = parseInt(this.getTotal(o), 10);
8985             if(!isNaN(vt)){
8986                 totalRecords = vt;
8987             }
8988         }
8989         if(s.successProperty){
8990             var vs = this.getSuccess(o);
8991             if(vs === false || vs === 'false'){
8992                 success = false;
8993             }
8994         }
8995         var records = [];
8996             for(var i = 0; i < c; i++){
8997                     var n = root[i];
8998                 var values = {};
8999                 var id = this.getId(n);
9000                 for(var j = 0; j < fl; j++){
9001                     f = fi[j];
9002                 var v = this.ef[j](n);
9003                 if (!f.convert) {
9004                     Roo.log('missing convert for ' + f.name);
9005                     Roo.log(f);
9006                     continue;
9007                 }
9008                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
9009                 }
9010                 var record = new Record(values, id);
9011                 record.json = n;
9012                 records[i] = record;
9013             }
9014             return {
9015             raw : o,
9016                 success : success,
9017                 records : records,
9018                 totalRecords : totalRecords
9019             };
9020     }
9021 });/*
9022  * Based on:
9023  * Ext JS Library 1.1.1
9024  * Copyright(c) 2006-2007, Ext JS, LLC.
9025  *
9026  * Originally Released Under LGPL - original licence link has changed is not relivant.
9027  *
9028  * Fork - LGPL
9029  * <script type="text/javascript">
9030  */
9031
9032 /**
9033  * @class Roo.data.ArrayReader
9034  * @extends Roo.data.DataReader
9035  * Data reader class to create an Array of Roo.data.Record objects from an Array.
9036  * Each element of that Array represents a row of data fields. The
9037  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
9038  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
9039  * <p>
9040  * Example code:.
9041  * <pre><code>
9042 var RecordDef = Roo.data.Record.create([
9043     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
9044     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
9045 ]);
9046 var myReader = new Roo.data.ArrayReader({
9047     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
9048 }, RecordDef);
9049 </code></pre>
9050  * <p>
9051  * This would consume an Array like this:
9052  * <pre><code>
9053 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
9054   </code></pre>
9055  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
9056  * @constructor
9057  * Create a new JsonReader
9058  * @param {Object} meta Metadata configuration options.
9059  * @param {Object} recordType Either an Array of field definition objects
9060  * as specified to {@link Roo.data.Record#create},
9061  * or an {@link Roo.data.Record} object
9062  * created using {@link Roo.data.Record#create}.
9063  */
9064 Roo.data.ArrayReader = function(meta, recordType){
9065     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
9066 };
9067
9068 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
9069     /**
9070      * Create a data block containing Roo.data.Records from an XML document.
9071      * @param {Object} o An Array of row objects which represents the dataset.
9072      * @return {Object} data A data block which is used by an Roo.data.Store object as
9073      * a cache of Roo.data.Records.
9074      */
9075     readRecords : function(o){
9076         var sid = this.meta ? this.meta.id : null;
9077         var recordType = this.recordType, fields = recordType.prototype.fields;
9078         var records = [];
9079         var root = o;
9080             for(var i = 0; i < root.length; i++){
9081                     var n = root[i];
9082                 var values = {};
9083                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
9084                 for(var j = 0, jlen = fields.length; j < jlen; j++){
9085                 var f = fields.items[j];
9086                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
9087                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
9088                 v = f.convert(v);
9089                 values[f.name] = v;
9090             }
9091                 var record = new recordType(values, id);
9092                 record.json = n;
9093                 records[records.length] = record;
9094             }
9095             return {
9096                 records : records,
9097                 totalRecords : records.length
9098             };
9099     }
9100 });/*
9101  * - LGPL
9102  * * 
9103  */
9104
9105 /**
9106  * @class Roo.bootstrap.ComboBox
9107  * @extends Roo.bootstrap.TriggerField
9108  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
9109  * @cfg {Boolean} append (true|false) default false
9110  * @constructor
9111  * Create a new ComboBox.
9112  * @param {Object} config Configuration options
9113  */
9114 Roo.bootstrap.ComboBox = function(config){
9115     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
9116     this.addEvents({
9117         /**
9118          * @event expand
9119          * Fires when the dropdown list is expanded
9120              * @param {Roo.bootstrap.ComboBox} combo This combo box
9121              */
9122         'expand' : true,
9123         /**
9124          * @event collapse
9125          * Fires when the dropdown list is collapsed
9126              * @param {Roo.bootstrap.ComboBox} combo This combo box
9127              */
9128         'collapse' : true,
9129         /**
9130          * @event beforeselect
9131          * Fires before a list item is selected. Return false to cancel the selection.
9132              * @param {Roo.bootstrap.ComboBox} combo This combo box
9133              * @param {Roo.data.Record} record The data record returned from the underlying store
9134              * @param {Number} index The index of the selected item in the dropdown list
9135              */
9136         'beforeselect' : true,
9137         /**
9138          * @event select
9139          * Fires when a list item is selected
9140              * @param {Roo.bootstrap.ComboBox} combo This combo box
9141              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
9142              * @param {Number} index The index of the selected item in the dropdown list
9143              */
9144         'select' : true,
9145         /**
9146          * @event beforequery
9147          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
9148          * The event object passed has these properties:
9149              * @param {Roo.bootstrap.ComboBox} combo This combo box
9150              * @param {String} query The query
9151              * @param {Boolean} forceAll true to force "all" query
9152              * @param {Boolean} cancel true to cancel the query
9153              * @param {Object} e The query event object
9154              */
9155         'beforequery': true,
9156          /**
9157          * @event add
9158          * Fires when the 'add' icon is pressed (add a listener to enable add button)
9159              * @param {Roo.bootstrap.ComboBox} combo This combo box
9160              */
9161         'add' : true,
9162         /**
9163          * @event edit
9164          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
9165              * @param {Roo.bootstrap.ComboBox} combo This combo box
9166              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
9167              */
9168         'edit' : true,
9169         /**
9170          * @event remove
9171          * Fires when the remove value from the combobox array
9172              * @param {Roo.bootstrap.ComboBox} combo This combo box
9173              */
9174         'remove' : true
9175         
9176     });
9177     
9178     
9179     this.selectedIndex = -1;
9180     if(this.mode == 'local'){
9181         if(config.queryDelay === undefined){
9182             this.queryDelay = 10;
9183         }
9184         if(config.minChars === undefined){
9185             this.minChars = 0;
9186         }
9187     }
9188 };
9189
9190 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
9191      
9192     /**
9193      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
9194      * rendering into an Roo.Editor, defaults to false)
9195      */
9196     /**
9197      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
9198      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
9199      */
9200     /**
9201      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
9202      */
9203     /**
9204      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
9205      * the dropdown list (defaults to undefined, with no header element)
9206      */
9207
9208      /**
9209      * @cfg {String/Roo.Template} tpl The template to use to render the output
9210      */
9211      
9212      /**
9213      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
9214      */
9215     listWidth: undefined,
9216     /**
9217      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
9218      * mode = 'remote' or 'text' if mode = 'local')
9219      */
9220     displayField: undefined,
9221     /**
9222      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
9223      * mode = 'remote' or 'value' if mode = 'local'). 
9224      * Note: use of a valueField requires the user make a selection
9225      * in order for a value to be mapped.
9226      */
9227     valueField: undefined,
9228     
9229     
9230     /**
9231      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
9232      * field's data value (defaults to the underlying DOM element's name)
9233      */
9234     hiddenName: undefined,
9235     /**
9236      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
9237      */
9238     listClass: '',
9239     /**
9240      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
9241      */
9242     selectedClass: 'active',
9243     
9244     /**
9245      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
9246      */
9247     shadow:'sides',
9248     /**
9249      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
9250      * anchor positions (defaults to 'tl-bl')
9251      */
9252     listAlign: 'tl-bl?',
9253     /**
9254      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
9255      */
9256     maxHeight: 300,
9257     /**
9258      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
9259      * query specified by the allQuery config option (defaults to 'query')
9260      */
9261     triggerAction: 'query',
9262     /**
9263      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
9264      * (defaults to 4, does not apply if editable = false)
9265      */
9266     minChars : 4,
9267     /**
9268      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
9269      * delay (typeAheadDelay) if it matches a known value (defaults to false)
9270      */
9271     typeAhead: false,
9272     /**
9273      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
9274      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
9275      */
9276     queryDelay: 500,
9277     /**
9278      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
9279      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
9280      */
9281     pageSize: 0,
9282     /**
9283      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
9284      * when editable = true (defaults to false)
9285      */
9286     selectOnFocus:false,
9287     /**
9288      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
9289      */
9290     queryParam: 'query',
9291     /**
9292      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
9293      * when mode = 'remote' (defaults to 'Loading...')
9294      */
9295     loadingText: 'Loading...',
9296     /**
9297      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
9298      */
9299     resizable: false,
9300     /**
9301      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
9302      */
9303     handleHeight : 8,
9304     /**
9305      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
9306      * traditional select (defaults to true)
9307      */
9308     editable: true,
9309     /**
9310      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
9311      */
9312     allQuery: '',
9313     /**
9314      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
9315      */
9316     mode: 'remote',
9317     /**
9318      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
9319      * listWidth has a higher value)
9320      */
9321     minListWidth : 70,
9322     /**
9323      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
9324      * allow the user to set arbitrary text into the field (defaults to false)
9325      */
9326     forceSelection:false,
9327     /**
9328      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
9329      * if typeAhead = true (defaults to 250)
9330      */
9331     typeAheadDelay : 250,
9332     /**
9333      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
9334      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
9335      */
9336     valueNotFoundText : undefined,
9337     /**
9338      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
9339      */
9340     blockFocus : false,
9341     
9342     /**
9343      * @cfg {Boolean} disableClear Disable showing of clear button.
9344      */
9345     disableClear : false,
9346     /**
9347      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
9348      */
9349     alwaysQuery : false,
9350     
9351     /**
9352      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
9353      */
9354     multiple : false,
9355     
9356     //private
9357     addicon : false,
9358     editicon: false,
9359     
9360     page: 0,
9361     hasQuery: false,
9362     append: false,
9363     loadNext: false,
9364     item: [],
9365     
9366     // element that contains real text value.. (when hidden is used..)
9367      
9368     // private
9369     initEvents: function(){
9370         
9371         if (!this.store) {
9372             throw "can not find store for combo";
9373         }
9374         this.store = Roo.factory(this.store, Roo.data);
9375         
9376         
9377         
9378         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
9379         
9380         
9381         if(this.hiddenName){
9382             
9383             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
9384             
9385             this.hiddenField.dom.value =
9386                 this.hiddenValue !== undefined ? this.hiddenValue :
9387                 this.value !== undefined ? this.value : '';
9388
9389             // prevent input submission
9390             this.el.dom.removeAttribute('name');
9391             this.hiddenField.dom.setAttribute('name', this.hiddenName);
9392              
9393              
9394         }
9395         //if(Roo.isGecko){
9396         //    this.el.dom.setAttribute('autocomplete', 'off');
9397         //}
9398
9399         var cls = 'x-combo-list';
9400         this.list = this.el.select('ul.dropdown-menu',true).first();
9401
9402         //this.list = new Roo.Layer({
9403         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
9404         //});
9405         
9406         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
9407         this.list.setWidth(lw);
9408         
9409         this.list.on('mouseover', this.onViewOver, this);
9410         this.list.on('mousemove', this.onViewMove, this);
9411         
9412         this.list.on('scroll', this.onViewScroll, this);
9413         
9414         /*
9415         this.list.swallowEvent('mousewheel');
9416         this.assetHeight = 0;
9417
9418         if(this.title){
9419             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
9420             this.assetHeight += this.header.getHeight();
9421         }
9422
9423         this.innerList = this.list.createChild({cls:cls+'-inner'});
9424         this.innerList.on('mouseover', this.onViewOver, this);
9425         this.innerList.on('mousemove', this.onViewMove, this);
9426         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
9427         
9428         if(this.allowBlank && !this.pageSize && !this.disableClear){
9429             this.footer = this.list.createChild({cls:cls+'-ft'});
9430             this.pageTb = new Roo.Toolbar(this.footer);
9431            
9432         }
9433         if(this.pageSize){
9434             this.footer = this.list.createChild({cls:cls+'-ft'});
9435             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
9436                     {pageSize: this.pageSize});
9437             
9438         }
9439         
9440         if (this.pageTb && this.allowBlank && !this.disableClear) {
9441             var _this = this;
9442             this.pageTb.add(new Roo.Toolbar.Fill(), {
9443                 cls: 'x-btn-icon x-btn-clear',
9444                 text: '&#160;',
9445                 handler: function()
9446                 {
9447                     _this.collapse();
9448                     _this.clearValue();
9449                     _this.onSelect(false, -1);
9450                 }
9451             });
9452         }
9453         if (this.footer) {
9454             this.assetHeight += this.footer.getHeight();
9455         }
9456         */
9457             
9458         if(!this.tpl){
9459             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
9460         }
9461
9462         this.view = new Roo.View(this.el.select('ul.dropdown-menu',true).first(), this.tpl, {
9463             singleSelect:true, store: this.store, selectedClass: this.selectedClass
9464         });
9465         //this.view.wrapEl.setDisplayed(false);
9466         this.view.on('click', this.onViewClick, this);
9467         
9468         
9469         
9470         this.store.on('beforeload', this.onBeforeLoad, this);
9471         this.store.on('load', this.onLoad, this);
9472         this.store.on('loadexception', this.onLoadException, this);
9473         /*
9474         if(this.resizable){
9475             this.resizer = new Roo.Resizable(this.list,  {
9476                pinned:true, handles:'se'
9477             });
9478             this.resizer.on('resize', function(r, w, h){
9479                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
9480                 this.listWidth = w;
9481                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
9482                 this.restrictHeight();
9483             }, this);
9484             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
9485         }
9486         */
9487         if(!this.editable){
9488             this.editable = true;
9489             this.setEditable(false);
9490         }
9491         
9492         /*
9493         
9494         if (typeof(this.events.add.listeners) != 'undefined') {
9495             
9496             this.addicon = this.wrap.createChild(
9497                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
9498        
9499             this.addicon.on('click', function(e) {
9500                 this.fireEvent('add', this);
9501             }, this);
9502         }
9503         if (typeof(this.events.edit.listeners) != 'undefined') {
9504             
9505             this.editicon = this.wrap.createChild(
9506                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
9507             if (this.addicon) {
9508                 this.editicon.setStyle('margin-left', '40px');
9509             }
9510             this.editicon.on('click', function(e) {
9511                 
9512                 // we fire even  if inothing is selected..
9513                 this.fireEvent('edit', this, this.lastData );
9514                 
9515             }, this);
9516         }
9517         */
9518         
9519         this.keyNav = new Roo.KeyNav(this.inputEl(), {
9520             "up" : function(e){
9521                 this.inKeyMode = true;
9522                 this.selectPrev();
9523             },
9524
9525             "down" : function(e){
9526                 if(!this.isExpanded()){
9527                     this.onTriggerClick();
9528                 }else{
9529                     this.inKeyMode = true;
9530                     this.selectNext();
9531                 }
9532             },
9533
9534             "enter" : function(e){
9535                 this.onViewClick();
9536                 //return true;
9537             },
9538
9539             "esc" : function(e){
9540                 this.collapse();
9541             },
9542
9543             "tab" : function(e){
9544                 this.collapse();
9545                 
9546                 if(this.fireEvent("specialkey", this, e)){
9547                     this.onViewClick(false);
9548                 }
9549                 
9550                 return true;
9551             },
9552
9553             scope : this,
9554
9555             doRelay : function(foo, bar, hname){
9556                 if(hname == 'down' || this.scope.isExpanded()){
9557                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
9558                 }
9559                 return true;
9560             },
9561
9562             forceKeyDown: true
9563         });
9564         
9565         
9566         this.queryDelay = Math.max(this.queryDelay || 10,
9567                 this.mode == 'local' ? 10 : 250);
9568         
9569         
9570         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
9571         
9572         if(this.typeAhead){
9573             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
9574         }
9575         if(this.editable !== false){
9576             this.inputEl().on("keyup", this.onKeyUp, this);
9577         }
9578         if(this.forceSelection){
9579             this.inputEl().on('blur', this.doForce, this);
9580         }
9581         
9582         if(this.multiple){
9583             this.choices = this.el.select('ul.select2-choices', true).first();
9584             this.searchField = this.el.select('ul li.select2-search-field', true).first();
9585         }
9586     },
9587
9588     onDestroy : function(){
9589         if(this.view){
9590             this.view.setStore(null);
9591             this.view.el.removeAllListeners();
9592             this.view.el.remove();
9593             this.view.purgeListeners();
9594         }
9595         if(this.list){
9596             this.list.dom.innerHTML  = '';
9597         }
9598         if(this.store){
9599             this.store.un('beforeload', this.onBeforeLoad, this);
9600             this.store.un('load', this.onLoad, this);
9601             this.store.un('loadexception', this.onLoadException, this);
9602         }
9603         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
9604     },
9605
9606     // private
9607     fireKey : function(e){
9608         if(e.isNavKeyPress() && !this.list.isVisible()){
9609             this.fireEvent("specialkey", this, e);
9610         }
9611     },
9612
9613     // private
9614     onResize: function(w, h){
9615 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
9616 //        
9617 //        if(typeof w != 'number'){
9618 //            // we do not handle it!?!?
9619 //            return;
9620 //        }
9621 //        var tw = this.trigger.getWidth();
9622 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
9623 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
9624 //        var x = w - tw;
9625 //        this.inputEl().setWidth( this.adjustWidth('input', x));
9626 //            
9627 //        //this.trigger.setStyle('left', x+'px');
9628 //        
9629 //        if(this.list && this.listWidth === undefined){
9630 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
9631 //            this.list.setWidth(lw);
9632 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
9633 //        }
9634         
9635     
9636         
9637     },
9638
9639     /**
9640      * Allow or prevent the user from directly editing the field text.  If false is passed,
9641      * the user will only be able to select from the items defined in the dropdown list.  This method
9642      * is the runtime equivalent of setting the 'editable' config option at config time.
9643      * @param {Boolean} value True to allow the user to directly edit the field text
9644      */
9645     setEditable : function(value){
9646         if(value == this.editable){
9647             return;
9648         }
9649         this.editable = value;
9650         if(!value){
9651             this.inputEl().dom.setAttribute('readOnly', true);
9652             this.inputEl().on('mousedown', this.onTriggerClick,  this);
9653             this.inputEl().addClass('x-combo-noedit');
9654         }else{
9655             this.inputEl().dom.setAttribute('readOnly', false);
9656             this.inputEl().un('mousedown', this.onTriggerClick,  this);
9657             this.inputEl().removeClass('x-combo-noedit');
9658         }
9659     },
9660
9661     // private
9662     
9663     onBeforeLoad : function(combo,opts){
9664         if(!this.hasFocus){
9665             return;
9666         }
9667          if (!opts.add) {
9668             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
9669          }
9670         this.restrictHeight();
9671         this.selectedIndex = -1;
9672     },
9673
9674     // private
9675     onLoad : function(){
9676         
9677         this.hasQuery = false;
9678         
9679         if(!this.hasFocus){
9680             return;
9681         }
9682         
9683         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
9684             this.loading.hide();
9685         }
9686         
9687         if(this.store.getCount() > 0){
9688             this.expand();
9689             this.restrictHeight();
9690             if(this.lastQuery == this.allQuery){
9691                 if(this.editable){
9692                     this.inputEl().dom.select();
9693                 }
9694                 if(!this.selectByValue(this.value, true)){
9695                     this.select(0, true);
9696                 }
9697             }else{
9698                 this.selectNext();
9699                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
9700                     this.taTask.delay(this.typeAheadDelay);
9701                 }
9702             }
9703         }else{
9704             this.onEmptyResults();
9705         }
9706         
9707         //this.el.focus();
9708     },
9709     // private
9710     onLoadException : function()
9711     {
9712         this.hasQuery = false;
9713         
9714         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
9715             this.loading.hide();
9716         }
9717         
9718         this.collapse();
9719         Roo.log(this.store.reader.jsonData);
9720         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
9721             // fixme
9722             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
9723         }
9724         
9725         
9726     },
9727     // private
9728     onTypeAhead : function(){
9729         if(this.store.getCount() > 0){
9730             var r = this.store.getAt(0);
9731             var newValue = r.data[this.displayField];
9732             var len = newValue.length;
9733             var selStart = this.getRawValue().length;
9734             
9735             if(selStart != len){
9736                 this.setRawValue(newValue);
9737                 this.selectText(selStart, newValue.length);
9738             }
9739         }
9740     },
9741
9742     // private
9743     onSelect : function(record, index){
9744         
9745         if(this.fireEvent('beforeselect', this, record, index) !== false){
9746         
9747             this.setFromData(index > -1 ? record.data : false);
9748             
9749             this.collapse();
9750             this.fireEvent('select', this, record, index);
9751         }
9752     },
9753
9754     /**
9755      * Returns the currently selected field value or empty string if no value is set.
9756      * @return {String} value The selected value
9757      */
9758     getValue : function(){
9759         
9760         if(this.multiple){
9761             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
9762         }
9763         
9764         if(this.valueField){
9765             return typeof this.value != 'undefined' ? this.value : '';
9766         }else{
9767             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
9768         }
9769     },
9770
9771     /**
9772      * Clears any text/value currently set in the field
9773      */
9774     clearValue : function(){
9775         if(this.hiddenField){
9776             this.hiddenField.dom.value = '';
9777         }
9778         this.value = '';
9779         this.setRawValue('');
9780         this.lastSelectionText = '';
9781         
9782     },
9783
9784     /**
9785      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
9786      * will be displayed in the field.  If the value does not match the data value of an existing item,
9787      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
9788      * Otherwise the field will be blank (although the value will still be set).
9789      * @param {String} value The value to match
9790      */
9791     setValue : function(v){
9792         if(this.multiple){
9793             this.syncValue();
9794             return;
9795         }
9796         
9797         var text = v;
9798         if(this.valueField){
9799             var r = this.findRecord(this.valueField, v);
9800             if(r){
9801                 text = r.data[this.displayField];
9802             }else if(this.valueNotFoundText !== undefined){
9803                 text = this.valueNotFoundText;
9804             }
9805         }
9806         this.lastSelectionText = text;
9807         if(this.hiddenField){
9808             this.hiddenField.dom.value = v;
9809         }
9810         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
9811         this.value = v;
9812     },
9813     /**
9814      * @property {Object} the last set data for the element
9815      */
9816     
9817     lastData : false,
9818     /**
9819      * Sets the value of the field based on a object which is related to the record format for the store.
9820      * @param {Object} value the value to set as. or false on reset?
9821      */
9822     setFromData : function(o){
9823         
9824         if(this.multiple){
9825             this.addItem(o);
9826             return;
9827         }
9828             
9829         var dv = ''; // display value
9830         var vv = ''; // value value..
9831         this.lastData = o;
9832         if (this.displayField) {
9833             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
9834         } else {
9835             // this is an error condition!!!
9836             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
9837         }
9838         
9839         if(this.valueField){
9840             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
9841         }
9842         
9843         if(this.hiddenField){
9844             this.hiddenField.dom.value = vv;
9845             
9846             this.lastSelectionText = dv;
9847             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
9848             this.value = vv;
9849             return;
9850         }
9851         // no hidden field.. - we store the value in 'value', but still display
9852         // display field!!!!
9853         this.lastSelectionText = dv;
9854         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
9855         this.value = vv;
9856         
9857         
9858     },
9859     // private
9860     reset : function(){
9861         // overridden so that last data is reset..
9862         this.setValue(this.originalValue);
9863         this.clearInvalid();
9864         this.lastData = false;
9865         if (this.view) {
9866             this.view.clearSelections();
9867         }
9868     },
9869     // private
9870     findRecord : function(prop, value){
9871         var record;
9872         if(this.store.getCount() > 0){
9873             this.store.each(function(r){
9874                 if(r.data[prop] == value){
9875                     record = r;
9876                     return false;
9877                 }
9878                 return true;
9879             });
9880         }
9881         return record;
9882     },
9883     
9884     getName: function()
9885     {
9886         // returns hidden if it's set..
9887         if (!this.rendered) {return ''};
9888         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
9889         
9890     },
9891     // private
9892     onViewMove : function(e, t){
9893         this.inKeyMode = false;
9894     },
9895
9896     // private
9897     onViewOver : function(e, t){
9898         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
9899             return;
9900         }
9901         var item = this.view.findItemFromChild(t);
9902         if(item){
9903             var index = this.view.indexOf(item);
9904             this.select(index, false);
9905         }
9906     },
9907
9908     // private
9909     onViewClick : function(doFocus)
9910     {
9911         var index = this.view.getSelectedIndexes()[0];
9912         var r = this.store.getAt(index);
9913         if(r){
9914             this.onSelect(r, index);
9915         }
9916         if(doFocus !== false && !this.blockFocus){
9917             this.inputEl().focus();
9918         }
9919     },
9920
9921     // private
9922     restrictHeight : function(){
9923         //this.innerList.dom.style.height = '';
9924         //var inner = this.innerList.dom;
9925         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
9926         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
9927         //this.list.beginUpdate();
9928         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
9929         this.list.alignTo(this.inputEl(), this.listAlign);
9930         //this.list.endUpdate();
9931     },
9932
9933     // private
9934     onEmptyResults : function(){
9935         this.collapse();
9936     },
9937
9938     /**
9939      * Returns true if the dropdown list is expanded, else false.
9940      */
9941     isExpanded : function(){
9942         return this.list.isVisible();
9943     },
9944
9945     /**
9946      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
9947      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
9948      * @param {String} value The data value of the item to select
9949      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
9950      * selected item if it is not currently in view (defaults to true)
9951      * @return {Boolean} True if the value matched an item in the list, else false
9952      */
9953     selectByValue : function(v, scrollIntoView){
9954         if(v !== undefined && v !== null){
9955             var r = this.findRecord(this.valueField || this.displayField, v);
9956             if(r){
9957                 this.select(this.store.indexOf(r), scrollIntoView);
9958                 return true;
9959             }
9960         }
9961         return false;
9962     },
9963
9964     /**
9965      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
9966      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
9967      * @param {Number} index The zero-based index of the list item to select
9968      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
9969      * selected item if it is not currently in view (defaults to true)
9970      */
9971     select : function(index, scrollIntoView){
9972         this.selectedIndex = index;
9973         this.view.select(index);
9974         if(scrollIntoView !== false){
9975             var el = this.view.getNode(index);
9976             if(el){
9977                 //this.innerList.scrollChildIntoView(el, false);
9978                 
9979             }
9980         }
9981     },
9982
9983     // private
9984     selectNext : function(){
9985         var ct = this.store.getCount();
9986         if(ct > 0){
9987             if(this.selectedIndex == -1){
9988                 this.select(0);
9989             }else if(this.selectedIndex < ct-1){
9990                 this.select(this.selectedIndex+1);
9991             }
9992         }
9993     },
9994
9995     // private
9996     selectPrev : function(){
9997         var ct = this.store.getCount();
9998         if(ct > 0){
9999             if(this.selectedIndex == -1){
10000                 this.select(0);
10001             }else if(this.selectedIndex != 0){
10002                 this.select(this.selectedIndex-1);
10003             }
10004         }
10005     },
10006
10007     // private
10008     onKeyUp : function(e){
10009         if(this.editable !== false && !e.isSpecialKey()){
10010             this.lastKey = e.getKey();
10011             this.dqTask.delay(this.queryDelay);
10012         }
10013     },
10014
10015     // private
10016     validateBlur : function(){
10017         return !this.list || !this.list.isVisible();   
10018     },
10019
10020     // private
10021     initQuery : function(){
10022         this.doQuery(this.getRawValue());
10023     },
10024
10025     // private
10026     doForce : function(){
10027         if(this.inputEl().dom.value.length > 0){
10028             this.inputEl().dom.value =
10029                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
10030              
10031         }
10032     },
10033
10034     /**
10035      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
10036      * query allowing the query action to be canceled if needed.
10037      * @param {String} query The SQL query to execute
10038      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
10039      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
10040      * saved in the current store (defaults to false)
10041      */
10042     doQuery : function(q, forceAll){
10043         
10044         if(q === undefined || q === null){
10045             q = '';
10046         }
10047         var qe = {
10048             query: q,
10049             forceAll: forceAll,
10050             combo: this,
10051             cancel:false
10052         };
10053         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
10054             return false;
10055         }
10056         q = qe.query;
10057         
10058         forceAll = qe.forceAll;
10059         if(forceAll === true || (q.length >= this.minChars)){
10060             
10061             this.hasQuery = true;
10062             
10063             if(this.lastQuery != q || this.alwaysQuery){
10064                 this.lastQuery = q;
10065                 if(this.mode == 'local'){
10066                     this.selectedIndex = -1;
10067                     if(forceAll){
10068                         this.store.clearFilter();
10069                     }else{
10070                         this.store.filter(this.displayField, q);
10071                     }
10072                     this.onLoad();
10073                 }else{
10074                     this.store.baseParams[this.queryParam] = q;
10075                     
10076                     var options = {params : this.getParams(q)};
10077                     
10078                     if(this.loadNext){
10079                         options.add = true;
10080                         options.params.start = this.page * this.pageSize;
10081                     }
10082                     
10083                     this.store.load(options);
10084                     this.expand();
10085                 }
10086             }else{
10087                 this.selectedIndex = -1;
10088                 this.onLoad();   
10089             }
10090         }
10091         
10092         this.loadNext = false;
10093     },
10094
10095     // private
10096     getParams : function(q){
10097         var p = {};
10098         //p[this.queryParam] = q;
10099         
10100         if(this.pageSize){
10101             p.start = 0;
10102             p.limit = this.pageSize;
10103         }
10104         return p;
10105     },
10106
10107     /**
10108      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
10109      */
10110     collapse : function(){
10111         if(!this.isExpanded()){
10112             return;
10113         }
10114         
10115         this.list.hide();
10116         Roo.get(document).un('mousedown', this.collapseIf, this);
10117         Roo.get(document).un('mousewheel', this.collapseIf, this);
10118         if (!this.editable) {
10119             Roo.get(document).un('keydown', this.listKeyPress, this);
10120         }
10121         this.fireEvent('collapse', this);
10122     },
10123
10124     // private
10125     collapseIf : function(e){
10126         var in_combo  = e.within(this.el);
10127         var in_list =  e.within(this.list);
10128         
10129         if (in_combo || in_list) {
10130             //e.stopPropagation();
10131             return;
10132         }
10133
10134         this.collapse();
10135         
10136     },
10137
10138     /**
10139      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
10140      */
10141     expand : function(){
10142        
10143         if(this.isExpanded() || !this.hasFocus){
10144             return;
10145         }
10146          Roo.log('expand');
10147         this.list.alignTo(this.inputEl(), this.listAlign);
10148         this.list.show();
10149         Roo.get(document).on('mousedown', this.collapseIf, this);
10150         Roo.get(document).on('mousewheel', this.collapseIf, this);
10151         if (!this.editable) {
10152             Roo.get(document).on('keydown', this.listKeyPress, this);
10153         }
10154         
10155         this.fireEvent('expand', this);
10156     },
10157
10158     // private
10159     // Implements the default empty TriggerField.onTriggerClick function
10160     onTriggerClick : function()
10161     {
10162         Roo.log('trigger click');
10163         
10164         if(this.disabled){
10165             return;
10166         }
10167         
10168         this.page = 0;
10169         this.loadNext = false;
10170         
10171         if(this.isExpanded()){
10172             this.collapse();
10173             if (!this.blockFocus) {
10174                 this.inputEl().focus();
10175             }
10176             
10177         }else {
10178             this.hasFocus = true;
10179             if(this.triggerAction == 'all') {
10180                 this.doQuery(this.allQuery, true);
10181             } else {
10182                 this.doQuery(this.getRawValue());
10183             }
10184             if (!this.blockFocus) {
10185                 this.inputEl().focus();
10186             }
10187         }
10188     },
10189     listKeyPress : function(e)
10190     {
10191         //Roo.log('listkeypress');
10192         // scroll to first matching element based on key pres..
10193         if (e.isSpecialKey()) {
10194             return false;
10195         }
10196         var k = String.fromCharCode(e.getKey()).toUpperCase();
10197         //Roo.log(k);
10198         var match  = false;
10199         var csel = this.view.getSelectedNodes();
10200         var cselitem = false;
10201         if (csel.length) {
10202             var ix = this.view.indexOf(csel[0]);
10203             cselitem  = this.store.getAt(ix);
10204             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
10205                 cselitem = false;
10206             }
10207             
10208         }
10209         
10210         this.store.each(function(v) { 
10211             if (cselitem) {
10212                 // start at existing selection.
10213                 if (cselitem.id == v.id) {
10214                     cselitem = false;
10215                 }
10216                 return true;
10217             }
10218                 
10219             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
10220                 match = this.store.indexOf(v);
10221                 return false;
10222             }
10223             return true;
10224         }, this);
10225         
10226         if (match === false) {
10227             return true; // no more action?
10228         }
10229         // scroll to?
10230         this.view.select(match);
10231         var sn = Roo.get(this.view.getSelectedNodes()[0])
10232         //sn.scrollIntoView(sn.dom.parentNode, false);
10233     },
10234     
10235     onViewScroll : function(e, t){
10236         
10237         if(this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
10238             return;
10239         }
10240         
10241         this.hasQuery = true;
10242         
10243         this.loading = this.list.select('.loading', true).first();
10244         
10245         if(this.loading === null){
10246             this.list.createChild({
10247                 tag: 'div',
10248                 cls: 'loading select2-more-results select2-active',
10249                 html: 'Loading more results...'
10250             })
10251             
10252             this.loading = this.list.select('.loading', true).first();
10253             
10254             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
10255             
10256             this.loading.hide();
10257         }
10258         
10259         this.loading.show();
10260         
10261         var _combo = this;
10262         
10263         this.page++;
10264         this.loadNext = true;
10265         
10266         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
10267         
10268         return;
10269     },
10270     
10271     addItem : function(o)
10272     {   
10273         var dv = ''; // display value
10274         
10275         if (this.displayField) {
10276             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
10277         } else {
10278             // this is an error condition!!!
10279             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
10280         }
10281         
10282         if(!dv.length){
10283             return;
10284         }
10285         
10286         var choice = this.choices.createChild({
10287             tag: 'li',
10288             cls: 'select2-search-choice',
10289             cn: [
10290                 {
10291                     tag: 'div',
10292                     html: dv
10293                 },
10294                 {
10295                     tag: 'a',
10296                     href: '#',
10297                     cls: 'select2-search-choice-close',
10298                     tabindex: '-1'
10299                 }
10300             ]
10301             
10302         }, this.searchField);
10303         
10304         var close = choice.select('a.select2-search-choice-close', true).first()
10305         
10306         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
10307         
10308         this.item.push(o);
10309         this.lastData = o;
10310         
10311         this.syncValue();
10312         
10313         this.inputEl().dom.value = '';
10314         
10315     },
10316     
10317     onRemoveItem : function(e, _self, o)
10318     {
10319         e.preventDefault();
10320         var index = this.item.indexOf(o.data) * 1;
10321         
10322         if( index < 0){
10323             Roo.log('not this item?!');
10324             return;
10325         }
10326         
10327         this.item.splice(index, 1);
10328         o.item.remove();
10329         
10330         this.syncValue();
10331         
10332         this.fireEvent('remove', this, e);
10333         
10334     },
10335     
10336     syncValue : function()
10337     {
10338         if(!this.item.length){
10339             this.clearValue();
10340             return;
10341         }
10342             
10343         var value = [];
10344         var _this = this;
10345         Roo.each(this.item, function(i){
10346             if(_this.valueField){
10347                 value.push(i[_this.valueField]);
10348                 return;
10349             }
10350
10351             value.push(i);
10352         });
10353
10354         this.value = value.join(',');
10355
10356         if(this.hiddenField){
10357             this.hiddenField.dom.value = this.value;
10358         }
10359     },
10360     
10361     clearItem : function()
10362     {
10363         if(!this.multiple){
10364             return;
10365         }
10366         
10367         this.item = [];
10368         
10369         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
10370            c.remove();
10371         });
10372         
10373         this.syncValue();
10374     }
10375     
10376     
10377
10378     /** 
10379     * @cfg {Boolean} grow 
10380     * @hide 
10381     */
10382     /** 
10383     * @cfg {Number} growMin 
10384     * @hide 
10385     */
10386     /** 
10387     * @cfg {Number} growMax 
10388     * @hide 
10389     */
10390     /**
10391      * @hide
10392      * @method autoSize
10393      */
10394 });
10395 /*
10396  * Based on:
10397  * Ext JS Library 1.1.1
10398  * Copyright(c) 2006-2007, Ext JS, LLC.
10399  *
10400  * Originally Released Under LGPL - original licence link has changed is not relivant.
10401  *
10402  * Fork - LGPL
10403  * <script type="text/javascript">
10404  */
10405
10406 /**
10407  * @class Roo.View
10408  * @extends Roo.util.Observable
10409  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
10410  * This class also supports single and multi selection modes. <br>
10411  * Create a data model bound view:
10412  <pre><code>
10413  var store = new Roo.data.Store(...);
10414
10415  var view = new Roo.View({
10416     el : "my-element",
10417     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
10418  
10419     singleSelect: true,
10420     selectedClass: "ydataview-selected",
10421     store: store
10422  });
10423
10424  // listen for node click?
10425  view.on("click", function(vw, index, node, e){
10426  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
10427  });
10428
10429  // load XML data
10430  dataModel.load("foobar.xml");
10431  </code></pre>
10432  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
10433  * <br><br>
10434  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
10435  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
10436  * 
10437  * Note: old style constructor is still suported (container, template, config)
10438  * 
10439  * @constructor
10440  * Create a new View
10441  * @param {Object} config The config object
10442  * 
10443  */
10444 Roo.View = function(config, depreciated_tpl, depreciated_config){
10445     
10446     if (typeof(depreciated_tpl) == 'undefined') {
10447         // new way.. - universal constructor.
10448         Roo.apply(this, config);
10449         this.el  = Roo.get(this.el);
10450     } else {
10451         // old format..
10452         this.el  = Roo.get(config);
10453         this.tpl = depreciated_tpl;
10454         Roo.apply(this, depreciated_config);
10455     }
10456     this.wrapEl  = this.el.wrap().wrap();
10457     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
10458     
10459     
10460     if(typeof(this.tpl) == "string"){
10461         this.tpl = new Roo.Template(this.tpl);
10462     } else {
10463         // support xtype ctors..
10464         this.tpl = new Roo.factory(this.tpl, Roo);
10465     }
10466     
10467     
10468     this.tpl.compile();
10469    
10470   
10471     
10472      
10473     /** @private */
10474     this.addEvents({
10475         /**
10476          * @event beforeclick
10477          * Fires before a click is processed. Returns false to cancel the default action.
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             "beforeclick" : true,
10484         /**
10485          * @event click
10486          * Fires when a template node is 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             "click" : true,
10493         /**
10494          * @event dblclick
10495          * Fires when a template node is double 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             "dblclick" : true,
10502         /**
10503          * @event contextmenu
10504          * Fires when a template node is right clicked.
10505          * @param {Roo.View} this
10506          * @param {Number} index The index of the target node
10507          * @param {HTMLElement} node The target node
10508          * @param {Roo.EventObject} e The raw event object
10509          */
10510             "contextmenu" : true,
10511         /**
10512          * @event selectionchange
10513          * Fires when the selected nodes change.
10514          * @param {Roo.View} this
10515          * @param {Array} selections Array of the selected nodes
10516          */
10517             "selectionchange" : true,
10518     
10519         /**
10520          * @event beforeselect
10521          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
10522          * @param {Roo.View} this
10523          * @param {HTMLElement} node The node to be selected
10524          * @param {Array} selections Array of currently selected nodes
10525          */
10526             "beforeselect" : true,
10527         /**
10528          * @event preparedata
10529          * Fires on every row to render, to allow you to change the data.
10530          * @param {Roo.View} this
10531          * @param {Object} data to be rendered (change this)
10532          */
10533           "preparedata" : true
10534           
10535           
10536         });
10537
10538
10539
10540     this.el.on({
10541         "click": this.onClick,
10542         "dblclick": this.onDblClick,
10543         "contextmenu": this.onContextMenu,
10544         scope:this
10545     });
10546
10547     this.selections = [];
10548     this.nodes = [];
10549     this.cmp = new Roo.CompositeElementLite([]);
10550     if(this.store){
10551         this.store = Roo.factory(this.store, Roo.data);
10552         this.setStore(this.store, true);
10553     }
10554     
10555     if ( this.footer && this.footer.xtype) {
10556            
10557          var fctr = this.wrapEl.appendChild(document.createElement("div"));
10558         
10559         this.footer.dataSource = this.store
10560         this.footer.container = fctr;
10561         this.footer = Roo.factory(this.footer, Roo);
10562         fctr.insertFirst(this.el);
10563         
10564         // this is a bit insane - as the paging toolbar seems to detach the el..
10565 //        dom.parentNode.parentNode.parentNode
10566          // they get detached?
10567     }
10568     
10569     
10570     Roo.View.superclass.constructor.call(this);
10571     
10572     
10573 };
10574
10575 Roo.extend(Roo.View, Roo.util.Observable, {
10576     
10577      /**
10578      * @cfg {Roo.data.Store} store Data store to load data from.
10579      */
10580     store : false,
10581     
10582     /**
10583      * @cfg {String|Roo.Element} el The container element.
10584      */
10585     el : '',
10586     
10587     /**
10588      * @cfg {String|Roo.Template} tpl The template used by this View 
10589      */
10590     tpl : false,
10591     /**
10592      * @cfg {String} dataName the named area of the template to use as the data area
10593      *                          Works with domtemplates roo-name="name"
10594      */
10595     dataName: false,
10596     /**
10597      * @cfg {String} selectedClass The css class to add to selected nodes
10598      */
10599     selectedClass : "x-view-selected",
10600      /**
10601      * @cfg {String} emptyText The empty text to show when nothing is loaded.
10602      */
10603     emptyText : "",
10604     
10605     /**
10606      * @cfg {String} text to display on mask (default Loading)
10607      */
10608     mask : false,
10609     /**
10610      * @cfg {Boolean} multiSelect Allow multiple selection
10611      */
10612     multiSelect : false,
10613     /**
10614      * @cfg {Boolean} singleSelect Allow single selection
10615      */
10616     singleSelect:  false,
10617     
10618     /**
10619      * @cfg {Boolean} toggleSelect - selecting 
10620      */
10621     toggleSelect : false,
10622     
10623     /**
10624      * Returns the element this view is bound to.
10625      * @return {Roo.Element}
10626      */
10627     getEl : function(){
10628         return this.wrapEl;
10629     },
10630     
10631     
10632
10633     /**
10634      * Refreshes the view. - called by datachanged on the store. - do not call directly.
10635      */
10636     refresh : function(){
10637         Roo.log('refresh');
10638         var t = this.tpl;
10639         
10640         // if we are using something like 'domtemplate', then
10641         // the what gets used is:
10642         // t.applySubtemplate(NAME, data, wrapping data..)
10643         // the outer template then get' applied with
10644         //     the store 'extra data'
10645         // and the body get's added to the
10646         //      roo-name="data" node?
10647         //      <span class='roo-tpl-{name}'></span> ?????
10648         
10649         
10650         
10651         this.clearSelections();
10652         this.el.update("");
10653         var html = [];
10654         var records = this.store.getRange();
10655         if(records.length < 1) {
10656             
10657             // is this valid??  = should it render a template??
10658             
10659             this.el.update(this.emptyText);
10660             return;
10661         }
10662         var el = this.el;
10663         if (this.dataName) {
10664             this.el.update(t.apply(this.store.meta)); //????
10665             el = this.el.child('.roo-tpl-' + this.dataName);
10666         }
10667         
10668         for(var i = 0, len = records.length; i < len; i++){
10669             var data = this.prepareData(records[i].data, i, records[i]);
10670             this.fireEvent("preparedata", this, data, i, records[i]);
10671             html[html.length] = Roo.util.Format.trim(
10672                 this.dataName ?
10673                     t.applySubtemplate(this.dataName, data, this.store.meta) :
10674                     t.apply(data)
10675             );
10676         }
10677         
10678         
10679         
10680         el.update(html.join(""));
10681         this.nodes = el.dom.childNodes;
10682         this.updateIndexes(0);
10683     },
10684     
10685
10686     /**
10687      * Function to override to reformat the data that is sent to
10688      * the template for each node.
10689      * DEPRICATED - use the preparedata event handler.
10690      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
10691      * a JSON object for an UpdateManager bound view).
10692      */
10693     prepareData : function(data, index, record)
10694     {
10695         this.fireEvent("preparedata", this, data, index, record);
10696         return data;
10697     },
10698
10699     onUpdate : function(ds, record){
10700          Roo.log('on update');   
10701         this.clearSelections();
10702         var index = this.store.indexOf(record);
10703         var n = this.nodes[index];
10704         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
10705         n.parentNode.removeChild(n);
10706         this.updateIndexes(index, index);
10707     },
10708
10709     
10710     
10711 // --------- FIXME     
10712     onAdd : function(ds, records, index)
10713     {
10714         Roo.log(['on Add', ds, records, index] );        
10715         this.clearSelections();
10716         if(this.nodes.length == 0){
10717             this.refresh();
10718             return;
10719         }
10720         var n = this.nodes[index];
10721         for(var i = 0, len = records.length; i < len; i++){
10722             var d = this.prepareData(records[i].data, i, records[i]);
10723             if(n){
10724                 this.tpl.insertBefore(n, d);
10725             }else{
10726                 
10727                 this.tpl.append(this.el, d);
10728             }
10729         }
10730         this.updateIndexes(index);
10731     },
10732
10733     onRemove : function(ds, record, index){
10734         Roo.log('onRemove');
10735         this.clearSelections();
10736         var el = this.dataName  ?
10737             this.el.child('.roo-tpl-' + this.dataName) :
10738             this.el; 
10739         
10740         el.dom.removeChild(this.nodes[index]);
10741         this.updateIndexes(index);
10742     },
10743
10744     /**
10745      * Refresh an individual node.
10746      * @param {Number} index
10747      */
10748     refreshNode : function(index){
10749         this.onUpdate(this.store, this.store.getAt(index));
10750     },
10751
10752     updateIndexes : function(startIndex, endIndex){
10753         var ns = this.nodes;
10754         startIndex = startIndex || 0;
10755         endIndex = endIndex || ns.length - 1;
10756         for(var i = startIndex; i <= endIndex; i++){
10757             ns[i].nodeIndex = i;
10758         }
10759     },
10760
10761     /**
10762      * Changes the data store this view uses and refresh the view.
10763      * @param {Store} store
10764      */
10765     setStore : function(store, initial){
10766         if(!initial && this.store){
10767             this.store.un("datachanged", this.refresh);
10768             this.store.un("add", this.onAdd);
10769             this.store.un("remove", this.onRemove);
10770             this.store.un("update", this.onUpdate);
10771             this.store.un("clear", this.refresh);
10772             this.store.un("beforeload", this.onBeforeLoad);
10773             this.store.un("load", this.onLoad);
10774             this.store.un("loadexception", this.onLoad);
10775         }
10776         if(store){
10777           
10778             store.on("datachanged", this.refresh, this);
10779             store.on("add", this.onAdd, this);
10780             store.on("remove", this.onRemove, this);
10781             store.on("update", this.onUpdate, this);
10782             store.on("clear", this.refresh, this);
10783             store.on("beforeload", this.onBeforeLoad, this);
10784             store.on("load", this.onLoad, this);
10785             store.on("loadexception", this.onLoad, this);
10786         }
10787         
10788         if(store){
10789             this.refresh();
10790         }
10791     },
10792     /**
10793      * onbeforeLoad - masks the loading area.
10794      *
10795      */
10796     onBeforeLoad : function(store,opts)
10797     {
10798          Roo.log('onBeforeLoad');   
10799         if (!opts.add) {
10800             this.el.update("");
10801         }
10802         this.el.mask(this.mask ? this.mask : "Loading" ); 
10803     },
10804     onLoad : function ()
10805     {
10806         this.el.unmask();
10807     },
10808     
10809
10810     /**
10811      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
10812      * @param {HTMLElement} node
10813      * @return {HTMLElement} The template node
10814      */
10815     findItemFromChild : function(node){
10816         var el = this.dataName  ?
10817             this.el.child('.roo-tpl-' + this.dataName,true) :
10818             this.el.dom; 
10819         
10820         if(!node || node.parentNode == el){
10821                     return node;
10822             }
10823             var p = node.parentNode;
10824             while(p && p != el){
10825             if(p.parentNode == el){
10826                 return p;
10827             }
10828             p = p.parentNode;
10829         }
10830             return null;
10831     },
10832
10833     /** @ignore */
10834     onClick : function(e){
10835         var item = this.findItemFromChild(e.getTarget());
10836         if(item){
10837             var index = this.indexOf(item);
10838             if(this.onItemClick(item, index, e) !== false){
10839                 this.fireEvent("click", this, index, item, e);
10840             }
10841         }else{
10842             this.clearSelections();
10843         }
10844     },
10845
10846     /** @ignore */
10847     onContextMenu : function(e){
10848         var item = this.findItemFromChild(e.getTarget());
10849         if(item){
10850             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
10851         }
10852     },
10853
10854     /** @ignore */
10855     onDblClick : function(e){
10856         var item = this.findItemFromChild(e.getTarget());
10857         if(item){
10858             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
10859         }
10860     },
10861
10862     onItemClick : function(item, index, e)
10863     {
10864         if(this.fireEvent("beforeclick", this, index, item, e) === false){
10865             return false;
10866         }
10867         if (this.toggleSelect) {
10868             var m = this.isSelected(item) ? 'unselect' : 'select';
10869             Roo.log(m);
10870             var _t = this;
10871             _t[m](item, true, false);
10872             return true;
10873         }
10874         if(this.multiSelect || this.singleSelect){
10875             if(this.multiSelect && e.shiftKey && this.lastSelection){
10876                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
10877             }else{
10878                 this.select(item, this.multiSelect && e.ctrlKey);
10879                 this.lastSelection = item;
10880             }
10881             e.preventDefault();
10882         }
10883         return true;
10884     },
10885
10886     /**
10887      * Get the number of selected nodes.
10888      * @return {Number}
10889      */
10890     getSelectionCount : function(){
10891         return this.selections.length;
10892     },
10893
10894     /**
10895      * Get the currently selected nodes.
10896      * @return {Array} An array of HTMLElements
10897      */
10898     getSelectedNodes : function(){
10899         return this.selections;
10900     },
10901
10902     /**
10903      * Get the indexes of the selected nodes.
10904      * @return {Array}
10905      */
10906     getSelectedIndexes : function(){
10907         var indexes = [], s = this.selections;
10908         for(var i = 0, len = s.length; i < len; i++){
10909             indexes.push(s[i].nodeIndex);
10910         }
10911         return indexes;
10912     },
10913
10914     /**
10915      * Clear all selections
10916      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
10917      */
10918     clearSelections : function(suppressEvent){
10919         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
10920             this.cmp.elements = this.selections;
10921             this.cmp.removeClass(this.selectedClass);
10922             this.selections = [];
10923             if(!suppressEvent){
10924                 this.fireEvent("selectionchange", this, this.selections);
10925             }
10926         }
10927     },
10928
10929     /**
10930      * Returns true if the passed node is selected
10931      * @param {HTMLElement/Number} node The node or node index
10932      * @return {Boolean}
10933      */
10934     isSelected : function(node){
10935         var s = this.selections;
10936         if(s.length < 1){
10937             return false;
10938         }
10939         node = this.getNode(node);
10940         return s.indexOf(node) !== -1;
10941     },
10942
10943     /**
10944      * Selects nodes.
10945      * @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
10946      * @param {Boolean} keepExisting (optional) true to keep existing selections
10947      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
10948      */
10949     select : function(nodeInfo, keepExisting, suppressEvent){
10950         if(nodeInfo instanceof Array){
10951             if(!keepExisting){
10952                 this.clearSelections(true);
10953             }
10954             for(var i = 0, len = nodeInfo.length; i < len; i++){
10955                 this.select(nodeInfo[i], true, true);
10956             }
10957             return;
10958         } 
10959         var node = this.getNode(nodeInfo);
10960         if(!node || this.isSelected(node)){
10961             return; // already selected.
10962         }
10963         if(!keepExisting){
10964             this.clearSelections(true);
10965         }
10966         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
10967             Roo.fly(node).addClass(this.selectedClass);
10968             this.selections.push(node);
10969             if(!suppressEvent){
10970                 this.fireEvent("selectionchange", this, this.selections);
10971             }
10972         }
10973         
10974         
10975     },
10976       /**
10977      * Unselects nodes.
10978      * @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
10979      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
10980      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
10981      */
10982     unselect : function(nodeInfo, keepExisting, suppressEvent)
10983     {
10984         if(nodeInfo instanceof Array){
10985             Roo.each(this.selections, function(s) {
10986                 this.unselect(s, nodeInfo);
10987             }, this);
10988             return;
10989         }
10990         var node = this.getNode(nodeInfo);
10991         if(!node || !this.isSelected(node)){
10992             Roo.log("not selected");
10993             return; // not selected.
10994         }
10995         // fireevent???
10996         var ns = [];
10997         Roo.each(this.selections, function(s) {
10998             if (s == node ) {
10999                 Roo.fly(node).removeClass(this.selectedClass);
11000
11001                 return;
11002             }
11003             ns.push(s);
11004         },this);
11005         
11006         this.selections= ns;
11007         this.fireEvent("selectionchange", this, this.selections);
11008     },
11009
11010     /**
11011      * Gets a template node.
11012      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
11013      * @return {HTMLElement} The node or null if it wasn't found
11014      */
11015     getNode : function(nodeInfo){
11016         if(typeof nodeInfo == "string"){
11017             return document.getElementById(nodeInfo);
11018         }else if(typeof nodeInfo == "number"){
11019             return this.nodes[nodeInfo];
11020         }
11021         return nodeInfo;
11022     },
11023
11024     /**
11025      * Gets a range template nodes.
11026      * @param {Number} startIndex
11027      * @param {Number} endIndex
11028      * @return {Array} An array of nodes
11029      */
11030     getNodes : function(start, end){
11031         var ns = this.nodes;
11032         start = start || 0;
11033         end = typeof end == "undefined" ? ns.length - 1 : end;
11034         var nodes = [];
11035         if(start <= end){
11036             for(var i = start; i <= end; i++){
11037                 nodes.push(ns[i]);
11038             }
11039         } else{
11040             for(var i = start; i >= end; i--){
11041                 nodes.push(ns[i]);
11042             }
11043         }
11044         return nodes;
11045     },
11046
11047     /**
11048      * Finds the index of the passed node
11049      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
11050      * @return {Number} The index of the node or -1
11051      */
11052     indexOf : function(node){
11053         node = this.getNode(node);
11054         if(typeof node.nodeIndex == "number"){
11055             return node.nodeIndex;
11056         }
11057         var ns = this.nodes;
11058         for(var i = 0, len = ns.length; i < len; i++){
11059             if(ns[i] == node){
11060                 return i;
11061             }
11062         }
11063         return -1;
11064     }
11065 });
11066 /*
11067  * - LGPL
11068  *
11069  * based on jquery fullcalendar
11070  * 
11071  */
11072
11073 Roo.bootstrap = Roo.bootstrap || {};
11074 /**
11075  * @class Roo.bootstrap.Calendar
11076  * @extends Roo.bootstrap.Component
11077  * Bootstrap Calendar class
11078  * @cfg {Boolean} loadMask (true|false) default false
11079  * @cfg {Object} header generate the user specific header of the calendar, default false
11080
11081  * @constructor
11082  * Create a new Container
11083  * @param {Object} config The config object
11084  */
11085
11086
11087
11088 Roo.bootstrap.Calendar = function(config){
11089     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
11090      this.addEvents({
11091         /**
11092              * @event select
11093              * Fires when a date is selected
11094              * @param {DatePicker} this
11095              * @param {Date} date The selected date
11096              */
11097         'select': true,
11098         /**
11099              * @event monthchange
11100              * Fires when the displayed month changes 
11101              * @param {DatePicker} this
11102              * @param {Date} date The selected month
11103              */
11104         'monthchange': true,
11105         /**
11106              * @event evententer
11107              * Fires when mouse over an event
11108              * @param {Calendar} this
11109              * @param {event} Event
11110              */
11111         'evententer': true,
11112         /**
11113              * @event eventleave
11114              * Fires when the mouse leaves an
11115              * @param {Calendar} this
11116              * @param {event}
11117              */
11118         'eventleave': true,
11119         /**
11120              * @event eventclick
11121              * Fires when the mouse click an
11122              * @param {Calendar} this
11123              * @param {event}
11124              */
11125         'eventclick': true
11126         
11127     });
11128
11129 };
11130
11131 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
11132     
11133      /**
11134      * @cfg {Number} startDay
11135      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
11136      */
11137     startDay : 0,
11138     
11139     loadMask : false,
11140     
11141     header : false,
11142       
11143     getAutoCreate : function(){
11144         
11145         
11146         var fc_button = function(name, corner, style, content ) {
11147             return Roo.apply({},{
11148                 tag : 'span',
11149                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
11150                          (corner.length ?
11151                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
11152                             ''
11153                         ),
11154                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
11155                 unselectable: 'on'
11156             });
11157         };
11158         
11159         var header = {};
11160         
11161         if(!this.header){
11162             header = {
11163                 tag : 'table',
11164                 cls : 'fc-header',
11165                 style : 'width:100%',
11166                 cn : [
11167                     {
11168                         tag: 'tr',
11169                         cn : [
11170                             {
11171                                 tag : 'td',
11172                                 cls : 'fc-header-left',
11173                                 cn : [
11174                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
11175                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
11176                                     { tag: 'span', cls: 'fc-header-space' },
11177                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
11178
11179
11180                                 ]
11181                             },
11182
11183                             {
11184                                 tag : 'td',
11185                                 cls : 'fc-header-center',
11186                                 cn : [
11187                                     {
11188                                         tag: 'span',
11189                                         cls: 'fc-header-title',
11190                                         cn : {
11191                                             tag: 'H2',
11192                                             html : 'month / year'
11193                                         }
11194                                     }
11195
11196                                 ]
11197                             },
11198                             {
11199                                 tag : 'td',
11200                                 cls : 'fc-header-right',
11201                                 cn : [
11202                               /*      fc_button('month', 'left', '', 'month' ),
11203                                     fc_button('week', '', '', 'week' ),
11204                                     fc_button('day', 'right', '', 'day' )
11205                                 */    
11206
11207                                 ]
11208                             }
11209
11210                         ]
11211                     }
11212                 ]
11213             };
11214         }
11215         
11216         header = this.header;
11217         
11218        
11219         var cal_heads = function() {
11220             var ret = [];
11221             // fixme - handle this.
11222             
11223             for (var i =0; i < Date.dayNames.length; i++) {
11224                 var d = Date.dayNames[i];
11225                 ret.push({
11226                     tag: 'th',
11227                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
11228                     html : d.substring(0,3)
11229                 });
11230                 
11231             }
11232             ret[0].cls += ' fc-first';
11233             ret[6].cls += ' fc-last';
11234             return ret;
11235         };
11236         var cal_cell = function(n) {
11237             return  {
11238                 tag: 'td',
11239                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
11240                 cn : [
11241                     {
11242                         cn : [
11243                             {
11244                                 cls: 'fc-day-number',
11245                                 html: 'D'
11246                             },
11247                             {
11248                                 cls: 'fc-day-content',
11249                              
11250                                 cn : [
11251                                      {
11252                                         style: 'position: relative;' // height: 17px;
11253                                     }
11254                                 ]
11255                             }
11256                             
11257                             
11258                         ]
11259                     }
11260                 ]
11261                 
11262             }
11263         };
11264         var cal_rows = function() {
11265             
11266             var ret = []
11267             for (var r = 0; r < 6; r++) {
11268                 var row= {
11269                     tag : 'tr',
11270                     cls : 'fc-week',
11271                     cn : []
11272                 };
11273                 
11274                 for (var i =0; i < Date.dayNames.length; i++) {
11275                     var d = Date.dayNames[i];
11276                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
11277
11278                 }
11279                 row.cn[0].cls+=' fc-first';
11280                 row.cn[0].cn[0].style = 'min-height:90px';
11281                 row.cn[6].cls+=' fc-last';
11282                 ret.push(row);
11283                 
11284             }
11285             ret[0].cls += ' fc-first';
11286             ret[4].cls += ' fc-prev-last';
11287             ret[5].cls += ' fc-last';
11288             return ret;
11289             
11290         };
11291         
11292         var cal_table = {
11293             tag: 'table',
11294             cls: 'fc-border-separate',
11295             style : 'width:100%',
11296             cellspacing  : 0,
11297             cn : [
11298                 { 
11299                     tag: 'thead',
11300                     cn : [
11301                         { 
11302                             tag: 'tr',
11303                             cls : 'fc-first fc-last',
11304                             cn : cal_heads()
11305                         }
11306                     ]
11307                 },
11308                 { 
11309                     tag: 'tbody',
11310                     cn : cal_rows()
11311                 }
11312                   
11313             ]
11314         };
11315          
11316          var cfg = {
11317             cls : 'fc fc-ltr',
11318             cn : [
11319                 header,
11320                 {
11321                     cls : 'fc-content',
11322                     style : "position: relative;",
11323                     cn : [
11324                         {
11325                             cls : 'fc-view fc-view-month fc-grid',
11326                             style : 'position: relative',
11327                             unselectable : 'on',
11328                             cn : [
11329                                 {
11330                                     cls : 'fc-event-container',
11331                                     style : 'position:absolute;z-index:8;top:0;left:0;'
11332                                 },
11333                                 cal_table
11334                             ]
11335                         }
11336                     ]
11337     
11338                 }
11339            ] 
11340             
11341         };
11342         
11343          
11344         
11345         return cfg;
11346     },
11347     
11348     
11349     initEvents : function()
11350     {
11351         if(!this.store){
11352             throw "can not find store for calendar";
11353         }
11354         
11355         var mark = {
11356             tag: "div",
11357             cls:"x-dlg-mask",
11358             style: "text-align:center",
11359             cn: [
11360                 {
11361                     tag: "div",
11362                     style: "background-color:white;width:50%;margin:250 auto",
11363                     cn: [
11364                         {
11365                             tag: "img",
11366                             src: rootURL + '/roojs1/images/ux/lightbox/loading.gif'
11367                         },
11368                         {
11369                             tag: "span",
11370                             html: "Loading"
11371                         }
11372                         
11373                     ]
11374                 }
11375             ]
11376         }
11377         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
11378         
11379         var size = this.el.select('.fc-content', true).first().getSize();
11380         this.maskEl.setSize(size.width, size.height);
11381         this.maskEl.enableDisplayMode("block");
11382         if(!this.loadMask){
11383             this.maskEl.hide();
11384         }
11385         
11386         this.store = Roo.factory(this.store, Roo.data);
11387         this.store.on('load', this.onLoad, this);
11388         this.store.on('beforeload', this.onBeforeLoad, this);
11389         
11390         this.resize();
11391         
11392         this.cells = this.el.select('.fc-day',true);
11393         //Roo.log(this.cells);
11394         this.textNodes = this.el.query('.fc-day-number');
11395         this.cells.addClassOnOver('fc-state-hover');
11396         
11397         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
11398         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
11399         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
11400         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
11401         
11402         this.on('monthchange', this.onMonthChange, this);
11403         
11404         this.update(new Date().clearTime());
11405     },
11406     
11407     resize : function() {
11408         var sz  = this.el.getSize();
11409         
11410         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
11411         this.el.select('.fc-day-content div',true).setHeight(34);
11412     },
11413     
11414     
11415     // private
11416     showPrevMonth : function(e){
11417         this.update(this.activeDate.add("mo", -1));
11418     },
11419     showToday : function(e){
11420         this.update(new Date().clearTime());
11421     },
11422     // private
11423     showNextMonth : function(e){
11424         this.update(this.activeDate.add("mo", 1));
11425     },
11426
11427     // private
11428     showPrevYear : function(){
11429         this.update(this.activeDate.add("y", -1));
11430     },
11431
11432     // private
11433     showNextYear : function(){
11434         this.update(this.activeDate.add("y", 1));
11435     },
11436
11437     
11438    // private
11439     update : function(date)
11440     {
11441         var vd = this.activeDate;
11442         this.activeDate = date;
11443 //        if(vd && this.el){
11444 //            var t = date.getTime();
11445 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
11446 //                Roo.log('using add remove');
11447 //                
11448 //                this.fireEvent('monthchange', this, date);
11449 //                
11450 //                this.cells.removeClass("fc-state-highlight");
11451 //                this.cells.each(function(c){
11452 //                   if(c.dateValue == t){
11453 //                       c.addClass("fc-state-highlight");
11454 //                       setTimeout(function(){
11455 //                            try{c.dom.firstChild.focus();}catch(e){}
11456 //                       }, 50);
11457 //                       return false;
11458 //                   }
11459 //                   return true;
11460 //                });
11461 //                return;
11462 //            }
11463 //        }
11464         
11465         var days = date.getDaysInMonth();
11466         
11467         var firstOfMonth = date.getFirstDateOfMonth();
11468         var startingPos = firstOfMonth.getDay()-this.startDay;
11469         
11470         if(startingPos < this.startDay){
11471             startingPos += 7;
11472         }
11473         
11474         var pm = date.add(Date.MONTH, -1);
11475         var prevStart = pm.getDaysInMonth()-startingPos;
11476 //        
11477         this.cells = this.el.select('.fc-day',true);
11478         this.textNodes = this.el.query('.fc-day-number');
11479         this.cells.addClassOnOver('fc-state-hover');
11480         
11481         var cells = this.cells.elements;
11482         var textEls = this.textNodes;
11483         
11484         Roo.each(cells, function(cell){
11485             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
11486         });
11487         
11488         days += startingPos;
11489
11490         // convert everything to numbers so it's fast
11491         var day = 86400000;
11492         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
11493         //Roo.log(d);
11494         //Roo.log(pm);
11495         //Roo.log(prevStart);
11496         
11497         var today = new Date().clearTime().getTime();
11498         var sel = date.clearTime().getTime();
11499         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
11500         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
11501         var ddMatch = this.disabledDatesRE;
11502         var ddText = this.disabledDatesText;
11503         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
11504         var ddaysText = this.disabledDaysText;
11505         var format = this.format;
11506         
11507         var setCellClass = function(cal, cell){
11508             
11509             //Roo.log('set Cell Class');
11510             cell.title = "";
11511             var t = d.getTime();
11512             
11513             //Roo.log(d);
11514             
11515             cell.dateValue = t;
11516             if(t == today){
11517                 cell.className += " fc-today";
11518                 cell.className += " fc-state-highlight";
11519                 cell.title = cal.todayText;
11520             }
11521             if(t == sel){
11522                 // disable highlight in other month..
11523                 //cell.className += " fc-state-highlight";
11524                 
11525             }
11526             // disabling
11527             if(t < min) {
11528                 cell.className = " fc-state-disabled";
11529                 cell.title = cal.minText;
11530                 return;
11531             }
11532             if(t > max) {
11533                 cell.className = " fc-state-disabled";
11534                 cell.title = cal.maxText;
11535                 return;
11536             }
11537             if(ddays){
11538                 if(ddays.indexOf(d.getDay()) != -1){
11539                     cell.title = ddaysText;
11540                     cell.className = " fc-state-disabled";
11541                 }
11542             }
11543             if(ddMatch && format){
11544                 var fvalue = d.dateFormat(format);
11545                 if(ddMatch.test(fvalue)){
11546                     cell.title = ddText.replace("%0", fvalue);
11547                     cell.className = " fc-state-disabled";
11548                 }
11549             }
11550             
11551             if (!cell.initialClassName) {
11552                 cell.initialClassName = cell.dom.className;
11553             }
11554             
11555             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
11556         };
11557
11558         var i = 0;
11559         
11560         for(; i < startingPos; i++) {
11561             textEls[i].innerHTML = (++prevStart);
11562             d.setDate(d.getDate()+1);
11563             
11564             cells[i].className = "fc-past fc-other-month";
11565             setCellClass(this, cells[i]);
11566         }
11567         
11568         var intDay = 0;
11569         
11570         for(; i < days; i++){
11571             intDay = i - startingPos + 1;
11572             textEls[i].innerHTML = (intDay);
11573             d.setDate(d.getDate()+1);
11574             
11575             cells[i].className = ''; // "x-date-active";
11576             setCellClass(this, cells[i]);
11577         }
11578         var extraDays = 0;
11579         
11580         for(; i < 42; i++) {
11581             textEls[i].innerHTML = (++extraDays);
11582             d.setDate(d.getDate()+1);
11583             
11584             cells[i].className = "fc-future fc-other-month";
11585             setCellClass(this, cells[i]);
11586         }
11587         
11588         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
11589         
11590         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
11591         
11592         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
11593         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
11594         
11595         if(totalRows != 6){
11596             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
11597             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
11598         }
11599         
11600         this.fireEvent('monthchange', this, date);
11601         
11602         
11603         /*
11604         if(!this.internalRender){
11605             var main = this.el.dom.firstChild;
11606             var w = main.offsetWidth;
11607             this.el.setWidth(w + this.el.getBorderWidth("lr"));
11608             Roo.fly(main).setWidth(w);
11609             this.internalRender = true;
11610             // opera does not respect the auto grow header center column
11611             // then, after it gets a width opera refuses to recalculate
11612             // without a second pass
11613             if(Roo.isOpera && !this.secondPass){
11614                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
11615                 this.secondPass = true;
11616                 this.update.defer(10, this, [date]);
11617             }
11618         }
11619         */
11620         
11621     },
11622     
11623     findCell : function(dt) {
11624         dt = dt.clearTime().getTime();
11625         var ret = false;
11626         this.cells.each(function(c){
11627             //Roo.log("check " +c.dateValue + '?=' + dt);
11628             if(c.dateValue == dt){
11629                 ret = c;
11630                 return false;
11631             }
11632             return true;
11633         });
11634         
11635         return ret;
11636     },
11637     
11638     findCells : function(ev) {
11639         var s = ev.start.clone().clearTime().getTime();
11640        // Roo.log(s);
11641         var e= ev.end.clone().clearTime().getTime();
11642        // Roo.log(e);
11643         var ret = [];
11644         this.cells.each(function(c){
11645              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
11646             
11647             if(c.dateValue > e){
11648                 return ;
11649             }
11650             if(c.dateValue < s){
11651                 return ;
11652             }
11653             ret.push(c);
11654         });
11655         
11656         return ret;    
11657     },
11658     
11659 //    findBestRow: function(cells)
11660 //    {
11661 //        var ret = 0;
11662 //        
11663 //        for (var i =0 ; i < cells.length;i++) {
11664 //            ret  = Math.max(cells[i].rows || 0,ret);
11665 //        }
11666 //        return ret;
11667 //        
11668 //    },
11669     
11670     
11671     addItem : function(ev)
11672     {
11673         // look for vertical location slot in
11674         var cells = this.findCells(ev);
11675         
11676 //        ev.row = this.findBestRow(cells);
11677         
11678         // work out the location.
11679         
11680         var crow = false;
11681         var rows = [];
11682         for(var i =0; i < cells.length; i++) {
11683             if (!crow) {
11684                 crow = {
11685                     start : cells[i],
11686                     end :  cells[i]
11687                 };
11688                 continue;
11689             }
11690             if (crow.start.getY() == cells[i].getY()) {
11691                 // on same row.
11692                 crow.end = cells[i];
11693                 continue;
11694             }
11695             // different row.
11696             rows.push(crow);
11697             crow = {
11698                 start: cells[i],
11699                 end : cells[i]
11700             };
11701             
11702         }
11703         
11704         rows.push(crow);
11705         ev.els = [];
11706         ev.rows = rows;
11707         ev.cells = cells;
11708         ev.rendered = false;
11709 //        for (var i = 0; i < cells.length;i++) {
11710 //            cells[i].rows = Math.max(cells[i].rows || 0 , ev.row + 1 );
11711 //            
11712 //        }
11713         
11714         this.calevents.push(ev);
11715     },
11716     
11717     clearEvents: function() {
11718         
11719         if(!this.calevents){
11720             return;
11721         }
11722         
11723         Roo.each(this.cells.elements, function(c){
11724             c.rows = [];
11725             c.more = [];
11726         });
11727         
11728         Roo.each(this.calevents, function(e) {
11729             Roo.each(e.els, function(el) {
11730                 el.un('mouseenter' ,this.onEventEnter, this);
11731                 el.un('mouseleave' ,this.onEventLeave, this);
11732                 el.remove();
11733             },this);
11734         },this);
11735         
11736         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
11737             e.remove();
11738         });
11739         
11740     },
11741     
11742     renderEvents: function()
11743     {   
11744         // first make sure there is enough space..
11745         this.cells.each(function(c) {
11746             c.rows = [];
11747             c.more = [];
11748         });
11749         
11750         for (var e = 0; e < this.calevents.length; e++) {
11751             
11752             var ev = this.calevents[e];
11753             var cells = ev.cells;
11754             var rows = ev.rows;
11755             
11756             for(var i = 0; i < cells.length; i++){
11757                 
11758                 var cbox = this.cells.item(this.cells.indexOf(cells[i]));
11759                 
11760                 if(cells.length < 2 && cbox.rows.length > 3){
11761                     cbox.more.push(ev);
11762                     continue;
11763                 }
11764                 
11765                 cbox.rows.push(ev);
11766             }
11767         }
11768         
11769         var _this = this;
11770         
11771         this.cells.each(function(c) {
11772             if(c.more.length && c.more.length == 1){
11773                 c.rows.push(c.more.pop());
11774             }
11775             
11776             var r = (c.more.length) ? c.rows.length + 1 : c.rows.length;
11777             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, r * 20));
11778             
11779             
11780             for (var e = 0; e < c.rows.length; e++){
11781                 var ev = c.rows[e];
11782                 
11783                 if(ev.rendered){
11784                     continue;
11785                 }
11786                 
11787                 var cells = ev.cells;
11788                 var rows = ev.rows;
11789                 
11790                 for(var i = 0; i < rows.length; i++) {
11791                 
11792                     // how many rows should it span..
11793
11794                     var  cfg = {
11795                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
11796                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
11797
11798                         unselectable : "on",
11799                         cn : [
11800                             {
11801                                 cls: 'fc-event-inner',
11802                                 cn : [
11803     //                                {
11804     //                                  tag:'span',
11805     //                                  cls: 'fc-event-time',
11806     //                                  html : cells.length > 1 ? '' : ev.time
11807     //                                },
11808                                     {
11809                                       tag:'span',
11810                                       cls: 'fc-event-title',
11811                                       html : String.format('{0}', ev.title)
11812                                     }
11813
11814
11815                                 ]
11816                             },
11817                             {
11818                                 cls: 'ui-resizable-handle ui-resizable-e',
11819                                 html : '&nbsp;&nbsp;&nbsp'
11820                             }
11821
11822                         ]
11823                     };
11824
11825                     if (i == 0) {
11826                         cfg.cls += ' fc-event-start';
11827                     }
11828                     if ((i+1) == rows.length) {
11829                         cfg.cls += ' fc-event-end';
11830                     }
11831
11832                     var ctr = _this.el.select('.fc-event-container',true).first();
11833                     var cg = ctr.createChild(cfg);
11834
11835                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
11836                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
11837
11838                     cg.setXY([sbox.x +2, sbox.y +(e * 20)]);    
11839                     cg.setWidth(ebox.right - sbox.x -2);
11840
11841                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
11842                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
11843                     cg.on('click', _this.onEventClick, _this, ev);
11844
11845                     ev.els.push(cg);
11846                     
11847                     ev.rendered = true;
11848                 }
11849                 
11850             }
11851             
11852             
11853             if(c.more.length){
11854                 var  cfg = {
11855                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
11856                     style : 'position: absolute',
11857                     unselectable : "on",
11858                     cn : [
11859                         {
11860                             cls: 'fc-event-inner',
11861                             cn : [
11862                                 {
11863                                   tag:'span',
11864                                   cls: 'fc-event-title',
11865                                   html : 'More'
11866                                 }
11867
11868
11869                             ]
11870                         },
11871                         {
11872                             cls: 'ui-resizable-handle ui-resizable-e',
11873                             html : '&nbsp;&nbsp;&nbsp'
11874                         }
11875
11876                     ]
11877                 };
11878
11879                 var ctr = _this.el.select('.fc-event-container',true).first();
11880                 var cg = ctr.createChild(cfg);
11881
11882                 var sbox = c.select('.fc-day-content',true).first().getBox();
11883                 var ebox = c.select('.fc-day-content',true).first().getBox();
11884                 //Roo.log(cg);
11885                 cg.setXY([sbox.x +2, sbox.y +(c.rows.length * 20)]);    
11886                 cg.setWidth(ebox.right - sbox.x -2);
11887
11888                 cg.on('click', _this.onMoreEventClick, _this, c.more);
11889                 
11890             }
11891             
11892         });
11893         
11894         
11895         
11896     },
11897     
11898     onEventEnter: function (e, el,event,d) {
11899         this.fireEvent('evententer', this, el, event);
11900     },
11901     
11902     onEventLeave: function (e, el,event,d) {
11903         this.fireEvent('eventleave', this, el, event);
11904     },
11905     
11906     onEventClick: function (e, el,event,d) {
11907         this.fireEvent('eventclick', this, el, event);
11908     },
11909     
11910     onMonthChange: function () {
11911         this.store.load();
11912     },
11913     
11914     onMoreEventClick: function(e, el, more)
11915     {
11916         var _this = this;
11917         
11918         this.calpopover.placement = 'right';
11919         this.calpopover.setTitle('More');
11920         
11921         this.calpopover.setContent('');
11922         
11923         var ctr = this.calpopover.el.select('.popover-content', true).first();
11924         
11925         Roo.each(more, function(m){
11926             var cfg = {
11927                 cls : 'fc-event-hori fc-event-draggable',
11928                 html : m.title
11929             }
11930             var cg = ctr.createChild(cfg);
11931             
11932             cg.on('click', _this.onEventClick, _this, m);
11933         });
11934         
11935         this.calpopover.show(el);
11936         
11937         
11938     },
11939     
11940     onLoad: function () 
11941     {   
11942         this.calevents = [];
11943         var cal = this;
11944         
11945         if(this.store.getCount() > 0){
11946             this.store.data.each(function(d){
11947                cal.addItem({
11948                     id : d.data.id,
11949                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
11950                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
11951                     time : d.data.start_time,
11952                     title : d.data.title,
11953                     description : d.data.description,
11954                     venue : d.data.venue
11955                 });
11956             });
11957         }
11958         
11959         this.renderEvents();
11960         
11961         if(this.calevents.length && this.loadMask){
11962             this.maskEl.hide();
11963         }
11964     },
11965     
11966     onBeforeLoad: function()
11967     {
11968         this.clearEvents();
11969         if(this.loadMask){
11970             this.maskEl.show();
11971         }
11972     }
11973 });
11974
11975  
11976  /*
11977  * - LGPL
11978  *
11979  * element
11980  * 
11981  */
11982
11983 /**
11984  * @class Roo.bootstrap.Popover
11985  * @extends Roo.bootstrap.Component
11986  * Bootstrap Popover class
11987  * @cfg {String} html contents of the popover   (or false to use children..)
11988  * @cfg {String} title of popover (or false to hide)
11989  * @cfg {String} placement how it is placed
11990  * @cfg {String} trigger click || hover (or false to trigger manually)
11991  * @cfg {String} over what (parent or false to trigger manually.)
11992  * 
11993  * @constructor
11994  * Create a new Popover
11995  * @param {Object} config The config object
11996  */
11997
11998 Roo.bootstrap.Popover = function(config){
11999     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
12000 };
12001
12002 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
12003     
12004     title: 'Fill in a title',
12005     html: false,
12006     
12007     placement : 'right',
12008     trigger : 'hover', // hover
12009     
12010     over: 'parent',
12011     
12012     can_build_overlaid : false,
12013     
12014     getChildContainer : function()
12015     {
12016         return this.el.select('.popover-content',true).first();
12017     },
12018     
12019     getAutoCreate : function(){
12020          Roo.log('make popover?');
12021         var cfg = {
12022            cls : 'popover roo-dynamic',
12023            style: 'display:block',
12024            cn : [
12025                 {
12026                     cls : 'arrow'
12027                 },
12028                 {
12029                     cls : 'popover-inner',
12030                     cn : [
12031                         {
12032                             tag: 'h3',
12033                             cls: 'popover-title',
12034                             html : this.title
12035                         },
12036                         {
12037                             cls : 'popover-content',
12038                             html : this.html
12039                         }
12040                     ]
12041                     
12042                 }
12043            ]
12044         };
12045         
12046         return cfg;
12047     },
12048     setTitle: function(str)
12049     {
12050         this.el.select('.popover-title',true).first().dom.innerHTML = str;
12051     },
12052     setContent: function(str)
12053     {
12054         this.el.select('.popover-content',true).first().dom.innerHTML = str;
12055     },
12056     // as it get's added to the bottom of the page.
12057     onRender : function(ct, position)
12058     {
12059         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
12060         if(!this.el){
12061             var cfg = Roo.apply({},  this.getAutoCreate());
12062             cfg.id = Roo.id();
12063             
12064             if (this.cls) {
12065                 cfg.cls += ' ' + this.cls;
12066             }
12067             if (this.style) {
12068                 cfg.style = this.style;
12069             }
12070             Roo.log("adding to ")
12071             this.el = Roo.get(document.body).createChild(cfg, position);
12072             Roo.log(this.el);
12073         }
12074         this.initEvents();
12075     },
12076     
12077     initEvents : function()
12078     {
12079         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
12080         this.el.enableDisplayMode('block');
12081         this.el.hide();
12082         if (this.over === false) {
12083             return; 
12084         }
12085         if (this.triggers === false) {
12086             return;
12087         }
12088         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
12089         var triggers = this.trigger ? this.trigger.split(' ') : [];
12090         Roo.each(triggers, function(trigger) {
12091         
12092             if (trigger == 'click') {
12093                 on_el.on('click', this.toggle, this);
12094             } else if (trigger != 'manual') {
12095                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
12096                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
12097       
12098                 on_el.on(eventIn  ,this.enter, this);
12099                 on_el.on(eventOut, this.leave, this);
12100             }
12101         }, this);
12102         
12103     },
12104     
12105     
12106     // private
12107     timeout : null,
12108     hoverState : null,
12109     
12110     toggle : function () {
12111         this.hoverState == 'in' ? this.leave() : this.enter();
12112     },
12113     
12114     enter : function () {
12115        
12116     
12117         clearTimeout(this.timeout);
12118     
12119         this.hoverState = 'in'
12120     
12121         if (!this.delay || !this.delay.show) {
12122             this.show();
12123             return 
12124         }
12125         var _t = this;
12126         this.timeout = setTimeout(function () {
12127             if (_t.hoverState == 'in') {
12128                 _t.show();
12129             }
12130         }, this.delay.show)
12131     },
12132     leave : function() {
12133         clearTimeout(this.timeout);
12134     
12135         this.hoverState = 'out'
12136     
12137         if (!this.delay || !this.delay.hide) {
12138             this.hide();
12139             return 
12140         }
12141         var _t = this;
12142         this.timeout = setTimeout(function () {
12143             if (_t.hoverState == 'out') {
12144                 _t.hide();
12145             }
12146         }, this.delay.hide)
12147     },
12148     
12149     show : function (on_el)
12150     {
12151         if (!on_el) {
12152             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
12153         }
12154         // set content.
12155         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
12156         if (this.html !== false) {
12157             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
12158         }
12159         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
12160         if (!this.title.length) {
12161             this.el.select('.popover-title',true).hide();
12162         }
12163         
12164         var placement = typeof this.placement == 'function' ?
12165             this.placement.call(this, this.el, on_el) :
12166             this.placement;
12167             
12168         var autoToken = /\s?auto?\s?/i;
12169         var autoPlace = autoToken.test(placement);
12170         if (autoPlace) {
12171             placement = placement.replace(autoToken, '') || 'top';
12172         }
12173         
12174         //this.el.detach()
12175         //this.el.setXY([0,0]);
12176         this.el.show();
12177         this.el.dom.style.display='block';
12178         this.el.addClass(placement);
12179         
12180         //this.el.appendTo(on_el);
12181         
12182         var p = this.getPosition();
12183         var box = this.el.getBox();
12184         
12185         if (autoPlace) {
12186             // fixme..
12187         }
12188         var align = Roo.bootstrap.Popover.alignment[placement]
12189         this.el.alignTo(on_el, align[0],align[1]);
12190         //var arrow = this.el.select('.arrow',true).first();
12191         //arrow.set(align[2], 
12192         
12193         this.el.addClass('in');
12194         this.hoverState = null;
12195         
12196         if (this.el.hasClass('fade')) {
12197             // fade it?
12198         }
12199         
12200     },
12201     hide : function()
12202     {
12203         this.el.setXY([0,0]);
12204         this.el.removeClass('in');
12205         this.el.hide();
12206         
12207     }
12208     
12209 });
12210
12211 Roo.bootstrap.Popover.alignment = {
12212     'left' : ['r-l', [-10,0], 'right'],
12213     'right' : ['l-r', [10,0], 'left'],
12214     'bottom' : ['t-b', [0,10], 'top'],
12215     'top' : [ 'b-t', [0,-10], 'bottom']
12216 };
12217
12218  /*
12219  * - LGPL
12220  *
12221  * Progress
12222  * 
12223  */
12224
12225 /**
12226  * @class Roo.bootstrap.Progress
12227  * @extends Roo.bootstrap.Component
12228  * Bootstrap Progress class
12229  * @cfg {Boolean} striped striped of the progress bar
12230  * @cfg {Boolean} active animated of the progress bar
12231  * 
12232  * 
12233  * @constructor
12234  * Create a new Progress
12235  * @param {Object} config The config object
12236  */
12237
12238 Roo.bootstrap.Progress = function(config){
12239     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
12240 };
12241
12242 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
12243     
12244     striped : false,
12245     active: false,
12246     
12247     getAutoCreate : function(){
12248         var cfg = {
12249             tag: 'div',
12250             cls: 'progress'
12251         };
12252         
12253         
12254         if(this.striped){
12255             cfg.cls += ' progress-striped';
12256         }
12257       
12258         if(this.active){
12259             cfg.cls += ' active';
12260         }
12261         
12262         
12263         return cfg;
12264     }
12265    
12266 });
12267
12268  
12269
12270  /*
12271  * - LGPL
12272  *
12273  * ProgressBar
12274  * 
12275  */
12276
12277 /**
12278  * @class Roo.bootstrap.ProgressBar
12279  * @extends Roo.bootstrap.Component
12280  * Bootstrap ProgressBar class
12281  * @cfg {Number} aria_valuenow aria-value now
12282  * @cfg {Number} aria_valuemin aria-value min
12283  * @cfg {Number} aria_valuemax aria-value max
12284  * @cfg {String} label label for the progress bar
12285  * @cfg {String} panel (success | info | warning | danger )
12286  * @cfg {String} role role of the progress bar
12287  * @cfg {String} sr_only text
12288  * 
12289  * 
12290  * @constructor
12291  * Create a new ProgressBar
12292  * @param {Object} config The config object
12293  */
12294
12295 Roo.bootstrap.ProgressBar = function(config){
12296     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
12297 };
12298
12299 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
12300     
12301     aria_valuenow : 0,
12302     aria_valuemin : 0,
12303     aria_valuemax : 100,
12304     label : false,
12305     panel : false,
12306     role : false,
12307     sr_only: false,
12308     
12309     getAutoCreate : function()
12310     {
12311         
12312         var cfg = {
12313             tag: 'div',
12314             cls: 'progress-bar',
12315             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
12316         };
12317         
12318         if(this.sr_only){
12319             cfg.cn = {
12320                 tag: 'span',
12321                 cls: 'sr-only',
12322                 html: this.sr_only
12323             }
12324         }
12325         
12326         if(this.role){
12327             cfg.role = this.role;
12328         }
12329         
12330         if(this.aria_valuenow){
12331             cfg['aria-valuenow'] = this.aria_valuenow;
12332         }
12333         
12334         if(this.aria_valuemin){
12335             cfg['aria-valuemin'] = this.aria_valuemin;
12336         }
12337         
12338         if(this.aria_valuemax){
12339             cfg['aria-valuemax'] = this.aria_valuemax;
12340         }
12341         
12342         if(this.label && !this.sr_only){
12343             cfg.html = this.label;
12344         }
12345         
12346         if(this.panel){
12347             cfg.cls += ' progress-bar-' + this.panel;
12348         }
12349         
12350         return cfg;
12351     },
12352     
12353     update : function(aria_valuenow)
12354     {
12355         this.aria_valuenow = aria_valuenow;
12356         
12357         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
12358     }
12359    
12360 });
12361
12362  
12363
12364  /*
12365  * - LGPL
12366  *
12367  * TabPanel
12368  * 
12369  */
12370
12371 /**
12372  * @class Roo.bootstrap.TabPanel
12373  * @extends Roo.bootstrap.Component
12374  * Bootstrap TabPanel class
12375  * @cfg {Boolean} active panel active
12376  * @cfg {String} html panel content
12377  * @cfg {String} tabId tab relate id
12378  * @cfg {String} navId The navbar which triggers show hide
12379  * 
12380  * 
12381  * @constructor
12382  * Create a new TabPanel
12383  * @param {Object} config The config object
12384  */
12385
12386 Roo.bootstrap.TabPanel = function(config){
12387     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
12388      this.addEvents({
12389         /**
12390              * @event changed
12391              * Fires when the active status changes
12392              * @param {Roo.bootstrap.TabPanel} this
12393              * @param {Boolean} state the new state
12394             
12395          */
12396         'changed': true
12397      });
12398 };
12399
12400 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
12401     
12402     active: false,
12403     html: false,
12404     tabId: false,
12405     navId : false,
12406     
12407     getAutoCreate : function(){
12408         var cfg = {
12409             tag: 'div',
12410             cls: 'tab-pane',
12411             html: this.html || ''
12412         };
12413         
12414         if(this.active){
12415             cfg.cls += ' active';
12416         }
12417         
12418         if(this.tabId){
12419             cfg.tabId = this.tabId;
12420         }
12421         
12422         return cfg;
12423     },
12424     onRender : function(ct, position)
12425     {
12426        // Roo.log("Call onRender: " + this.xtype);
12427         
12428         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
12429         
12430         if (this.navId && this.tabId) {
12431             var item = Roo.bootstrap.NavGroup.get(this.navId).getNavItem(this.tabId);
12432             if (!item) {
12433                 Roo.log("could not find navID:"  + this.navId + ", tabId: " + this.tabId);
12434             } else {
12435                 item.on('changed', function(item, state) {
12436                     this.setActive(state);
12437                 }, this);
12438             }
12439         }
12440         
12441     },
12442     setActive: function(state)
12443     {
12444         Roo.log("panel - set active " + this.tabId + "=" + state);
12445         
12446         this.active = state;
12447         if (!state) {
12448             this.el.removeClass('active');
12449             
12450         } else  if (!this.el.hasClass('active')) {
12451             this.el.addClass('active');
12452         }
12453         this.fireEvent('changed', this, state);
12454     }
12455     
12456     
12457 });
12458  
12459
12460  
12461
12462  /*
12463  * - LGPL
12464  *
12465  * DateField
12466  * 
12467  */
12468
12469 /**
12470  * @class Roo.bootstrap.DateField
12471  * @extends Roo.bootstrap.Input
12472  * Bootstrap DateField class
12473  * @cfg {Number} weekStart default 0
12474  * @cfg {Number} weekStart default 0
12475  * @cfg {Number} viewMode default empty, (months|years)
12476  * @cfg {Number} minViewMode default empty, (months|years)
12477  * @cfg {Number} startDate default -Infinity
12478  * @cfg {Number} endDate default Infinity
12479  * @cfg {Boolean} todayHighlight default false
12480  * @cfg {Boolean} todayBtn default false
12481  * @cfg {Boolean} calendarWeeks default false
12482  * @cfg {Object} daysOfWeekDisabled default empty
12483  * 
12484  * @cfg {Boolean} keyboardNavigation default true
12485  * @cfg {String} language default en
12486  * 
12487  * @constructor
12488  * Create a new DateField
12489  * @param {Object} config The config object
12490  */
12491
12492 Roo.bootstrap.DateField = function(config){
12493     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
12494      this.addEvents({
12495             /**
12496              * @event show
12497              * Fires when this field show.
12498              * @param {Roo.bootstrap.DateField} this
12499              * @param {Mixed} date The date value
12500              */
12501             show : true,
12502             /**
12503              * @event show
12504              * Fires when this field hide.
12505              * @param {Roo.bootstrap.DateField} this
12506              * @param {Mixed} date The date value
12507              */
12508             hide : true,
12509             /**
12510              * @event select
12511              * Fires when select a date.
12512              * @param {Roo.bootstrap.DateField} this
12513              * @param {Mixed} date The date value
12514              */
12515             select : true
12516         });
12517 };
12518
12519 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
12520     
12521     /**
12522      * @cfg {String} format
12523      * The default date format string which can be overriden for localization support.  The format must be
12524      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
12525      */
12526     format : "m/d/y",
12527     /**
12528      * @cfg {String} altFormats
12529      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
12530      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
12531      */
12532     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
12533     
12534     weekStart : 0,
12535     
12536     viewMode : '',
12537     
12538     minViewMode : '',
12539     
12540     todayHighlight : false,
12541     
12542     todayBtn: false,
12543     
12544     language: 'en',
12545     
12546     keyboardNavigation: true,
12547     
12548     calendarWeeks: false,
12549     
12550     startDate: -Infinity,
12551     
12552     endDate: Infinity,
12553     
12554     daysOfWeekDisabled: [],
12555     
12556     _events: [],
12557     
12558     UTCDate: function()
12559     {
12560         return new Date(Date.UTC.apply(Date, arguments));
12561     },
12562     
12563     UTCToday: function()
12564     {
12565         var today = new Date();
12566         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
12567     },
12568     
12569     getDate: function() {
12570             var d = this.getUTCDate();
12571             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
12572     },
12573     
12574     getUTCDate: function() {
12575             return this.date;
12576     },
12577     
12578     setDate: function(d) {
12579             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
12580     },
12581     
12582     setUTCDate: function(d) {
12583             this.date = d;
12584             this.setValue(this.formatDate(this.date));
12585     },
12586         
12587     onRender: function(ct, position)
12588     {
12589         
12590         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
12591         
12592         this.language = this.language || 'en';
12593         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
12594         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
12595         
12596         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
12597         this.format = this.format || 'm/d/y';
12598         this.isInline = false;
12599         this.isInput = true;
12600         this.component = this.el.select('.add-on', true).first() || false;
12601         this.component = (this.component && this.component.length === 0) ? false : this.component;
12602         this.hasInput = this.component && this.inputEL().length;
12603         
12604         if (typeof(this.minViewMode === 'string')) {
12605             switch (this.minViewMode) {
12606                 case 'months':
12607                     this.minViewMode = 1;
12608                     break;
12609                 case 'years':
12610                     this.minViewMode = 2;
12611                     break;
12612                 default:
12613                     this.minViewMode = 0;
12614                     break;
12615             }
12616         }
12617         
12618         if (typeof(this.viewMode === 'string')) {
12619             switch (this.viewMode) {
12620                 case 'months':
12621                     this.viewMode = 1;
12622                     break;
12623                 case 'years':
12624                     this.viewMode = 2;
12625                     break;
12626                 default:
12627                     this.viewMode = 0;
12628                     break;
12629             }
12630         }
12631                 
12632         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
12633         
12634         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
12635         
12636         this.picker().on('mousedown', this.onMousedown, this);
12637         this.picker().on('click', this.onClick, this);
12638         
12639         this.picker().addClass('datepicker-dropdown');
12640         
12641         this.startViewMode = this.viewMode;
12642         
12643         
12644         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
12645             if(!this.calendarWeeks){
12646                 v.remove();
12647                 return;
12648             };
12649             
12650             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
12651             v.attr('colspan', function(i, val){
12652                 return parseInt(val) + 1;
12653             });
12654         })
12655                         
12656         
12657         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
12658         
12659         this.setStartDate(this.startDate);
12660         this.setEndDate(this.endDate);
12661         
12662         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
12663         
12664         this.fillDow();
12665         this.fillMonths();
12666         this.update();
12667         this.showMode();
12668         
12669         if(this.isInline) {
12670             this.show();
12671         }
12672     },
12673     
12674     picker : function()
12675     {
12676         return this.el.select('.datepicker', true).first();
12677     },
12678     
12679     fillDow: function()
12680     {
12681         var dowCnt = this.weekStart;
12682         
12683         var dow = {
12684             tag: 'tr',
12685             cn: [
12686                 
12687             ]
12688         };
12689         
12690         if(this.calendarWeeks){
12691             dow.cn.push({
12692                 tag: 'th',
12693                 cls: 'cw',
12694                 html: '&nbsp;'
12695             })
12696         }
12697         
12698         while (dowCnt < this.weekStart + 7) {
12699             dow.cn.push({
12700                 tag: 'th',
12701                 cls: 'dow',
12702                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
12703             });
12704         }
12705         
12706         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
12707     },
12708     
12709     fillMonths: function()
12710     {    
12711         var i = 0
12712         var months = this.picker().select('>.datepicker-months td', true).first();
12713         
12714         months.dom.innerHTML = '';
12715         
12716         while (i < 12) {
12717             var month = {
12718                 tag: 'span',
12719                 cls: 'month',
12720                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
12721             }
12722             
12723             months.createChild(month);
12724         }
12725         
12726     },
12727     
12728     update: function(){
12729         
12730         this.date = (typeof(this.date) === 'undefined') ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
12731         
12732         if (this.date < this.startDate) {
12733             this.viewDate = new Date(this.startDate);
12734         } else if (this.date > this.endDate) {
12735             this.viewDate = new Date(this.endDate);
12736         } else {
12737             this.viewDate = new Date(this.date);
12738         }
12739         
12740         this.fill();
12741     },
12742     
12743     fill: function() {
12744         var d = new Date(this.viewDate),
12745                 year = d.getUTCFullYear(),
12746                 month = d.getUTCMonth(),
12747                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
12748                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
12749                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
12750                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
12751                 currentDate = this.date && this.date.valueOf(),
12752                 today = this.UTCToday();
12753         
12754         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
12755         
12756 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
12757         
12758 //        this.picker.select('>tfoot th.today').
12759 //                                              .text(dates[this.language].today)
12760 //                                              .toggle(this.todayBtn !== false);
12761     
12762         this.updateNavArrows();
12763         this.fillMonths();
12764                                                 
12765         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
12766         
12767         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
12768          
12769         prevMonth.setUTCDate(day);
12770         
12771         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
12772         
12773         var nextMonth = new Date(prevMonth);
12774         
12775         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
12776         
12777         nextMonth = nextMonth.valueOf();
12778         
12779         var fillMonths = false;
12780         
12781         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
12782         
12783         while(prevMonth.valueOf() < nextMonth) {
12784             var clsName = '';
12785             
12786             if (prevMonth.getUTCDay() === this.weekStart) {
12787                 if(fillMonths){
12788                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
12789                 }
12790                     
12791                 fillMonths = {
12792                     tag: 'tr',
12793                     cn: []
12794                 };
12795                 
12796                 if(this.calendarWeeks){
12797                     // ISO 8601: First week contains first thursday.
12798                     // ISO also states week starts on Monday, but we can be more abstract here.
12799                     var
12800                     // Start of current week: based on weekstart/current date
12801                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
12802                     // Thursday of this week
12803                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
12804                     // First Thursday of year, year from thursday
12805                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
12806                     // Calendar week: ms between thursdays, div ms per day, div 7 days
12807                     calWeek =  (th - yth) / 864e5 / 7 + 1;
12808                     
12809                     fillMonths.cn.push({
12810                         tag: 'td',
12811                         cls: 'cw',
12812                         html: calWeek
12813                     });
12814                 }
12815             }
12816             
12817             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
12818                 clsName += ' old';
12819             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
12820                 clsName += ' new';
12821             }
12822             if (this.todayHighlight &&
12823                 prevMonth.getUTCFullYear() == today.getFullYear() &&
12824                 prevMonth.getUTCMonth() == today.getMonth() &&
12825                 prevMonth.getUTCDate() == today.getDate()) {
12826                 clsName += ' today';
12827             }
12828             
12829             if (currentDate && prevMonth.valueOf() === currentDate) {
12830                 clsName += ' active';
12831             }
12832             
12833             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
12834                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
12835                     clsName += ' disabled';
12836             }
12837             
12838             fillMonths.cn.push({
12839                 tag: 'td',
12840                 cls: 'day ' + clsName,
12841                 html: prevMonth.getDate()
12842             })
12843             
12844             prevMonth.setDate(prevMonth.getDate()+1);
12845         }
12846           
12847         var currentYear = this.date && this.date.getUTCFullYear();
12848         var currentMonth = this.date && this.date.getUTCMonth();
12849         
12850         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
12851         
12852         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
12853             v.removeClass('active');
12854             
12855             if(currentYear === year && k === currentMonth){
12856                 v.addClass('active');
12857             }
12858             
12859             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
12860                 v.addClass('disabled');
12861             }
12862             
12863         });
12864         
12865         
12866         year = parseInt(year/10, 10) * 10;
12867         
12868         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
12869         
12870         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
12871         
12872         year -= 1;
12873         for (var i = -1; i < 11; i++) {
12874             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
12875                 tag: 'span',
12876                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
12877                 html: year
12878             })
12879             
12880             year += 1;
12881         }
12882     },
12883     
12884     showMode: function(dir) {
12885         if (dir) {
12886             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
12887         }
12888         Roo.each(this.picker().select('>div',true).elements, function(v){
12889             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
12890             v.hide();
12891         });
12892         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
12893     },
12894     
12895     place: function()
12896     {
12897         if(this.isInline) return;
12898         
12899         this.picker().removeClass(['bottom', 'top']);
12900         
12901         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
12902             /*
12903              * place to the top of element!
12904              *
12905              */
12906             
12907             this.picker().addClass('top');
12908             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12909             
12910             return;
12911         }
12912         
12913         this.picker().addClass('bottom');
12914         
12915         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12916     },
12917     
12918     parseDate : function(value){
12919         if(!value || value instanceof Date){
12920             return value;
12921         }
12922         var v = Date.parseDate(value, this.format);
12923         if (!v && this.useIso) {
12924             v = Date.parseDate(value, 'Y-m-d');
12925         }
12926         if(!v && this.altFormats){
12927             if(!this.altFormatsArray){
12928                 this.altFormatsArray = this.altFormats.split("|");
12929             }
12930             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
12931                 v = Date.parseDate(value, this.altFormatsArray[i]);
12932             }
12933         }
12934         return v;
12935     },
12936     
12937     formatDate : function(date, fmt){
12938         return (!date || !(date instanceof Date)) ?
12939         date : date.dateFormat(fmt || this.format);
12940     },
12941     
12942     onFocus : function()
12943     {
12944         Roo.bootstrap.DateField.superclass.onFocus.call(this);
12945         this.show();
12946     },
12947     
12948     onBlur : function()
12949     {
12950         Roo.bootstrap.DateField.superclass.onBlur.call(this);
12951         this.hide();
12952     },
12953     
12954     show : function()
12955     {
12956         this.picker().show();
12957         this.update();
12958         this.place();
12959         
12960         this.fireEvent('show', this, this.date);
12961     },
12962     
12963     hide : function()
12964     {
12965         if(this.isInline) return;
12966         this.picker().hide();
12967         this.viewMode = this.startViewMode;
12968         this.showMode();
12969         
12970         this.fireEvent('hide', this, this.date);
12971         
12972     },
12973     
12974     onMousedown: function(e){
12975         e.stopPropagation();
12976         e.preventDefault();
12977     },
12978     
12979     keyup: function(e){
12980         Roo.bootstrap.DateField.superclass.keyup.call(this);
12981         this.update();
12982         
12983     },
12984
12985     setValue: function(v){
12986         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
12987         
12988         this.fireEvent('select', this, this.date);
12989         
12990     },
12991     
12992     fireKey: function(e){
12993         if (!this.picker().isVisible()){
12994             if (e.keyCode == 27) // allow escape to hide and re-show picker
12995                 this.show();
12996             return;
12997         }
12998         var dateChanged = false,
12999         dir, day, month,
13000         newDate, newViewDate;
13001         switch(e.keyCode){
13002             case 27: // escape
13003                 this.hide();
13004                 e.preventDefault();
13005                 break;
13006             case 37: // left
13007             case 39: // right
13008                 if (!this.keyboardNavigation) break;
13009                 dir = e.keyCode == 37 ? -1 : 1;
13010                 
13011                 if (e.ctrlKey){
13012                     newDate = this.moveYear(this.date, dir);
13013                     newViewDate = this.moveYear(this.viewDate, dir);
13014                 } else if (e.shiftKey){
13015                     newDate = this.moveMonth(this.date, dir);
13016                     newViewDate = this.moveMonth(this.viewDate, dir);
13017                 } else {
13018                     newDate = new Date(this.date);
13019                     newDate.setUTCDate(this.date.getUTCDate() + dir);
13020                     newViewDate = new Date(this.viewDate);
13021                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
13022                 }
13023                 if (this.dateWithinRange(newDate)){
13024                     this.date = newDate;
13025                     this.viewDate = newViewDate;
13026                     this.setValue(this.formatDate(this.date));
13027                     this.update();
13028                     e.preventDefault();
13029                     dateChanged = true;
13030                 }
13031                 break;
13032             case 38: // up
13033             case 40: // down
13034                 if (!this.keyboardNavigation) break;
13035                 dir = e.keyCode == 38 ? -1 : 1;
13036                 if (e.ctrlKey){
13037                     newDate = this.moveYear(this.date, dir);
13038                     newViewDate = this.moveYear(this.viewDate, dir);
13039                 } else if (e.shiftKey){
13040                     newDate = this.moveMonth(this.date, dir);
13041                     newViewDate = this.moveMonth(this.viewDate, dir);
13042                 } else {
13043                     newDate = new Date(this.date);
13044                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
13045                     newViewDate = new Date(this.viewDate);
13046                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
13047                 }
13048                 if (this.dateWithinRange(newDate)){
13049                     this.date = newDate;
13050                     this.viewDate = newViewDate;
13051                     this.setValue(this.formatDate(this.date));
13052                     this.update();
13053                     e.preventDefault();
13054                     dateChanged = true;
13055                 }
13056                 break;
13057             case 13: // enter
13058                 this.setValue(this.formatDate(this.date));
13059                 this.hide();
13060                 e.preventDefault();
13061                 break;
13062             case 9: // tab
13063                 this.setValue(this.formatDate(this.date));
13064                 this.hide();
13065                 break;
13066         }
13067     },
13068     
13069     
13070     onClick: function(e) {
13071         e.stopPropagation();
13072         e.preventDefault();
13073         
13074         var target = e.getTarget();
13075         
13076         if(target.nodeName.toLowerCase() === 'i'){
13077             target = Roo.get(target).dom.parentNode;
13078         }
13079         
13080         var nodeName = target.nodeName;
13081         var className = target.className;
13082         var html = target.innerHTML;
13083         
13084         switch(nodeName.toLowerCase()) {
13085             case 'th':
13086                 switch(className) {
13087                     case 'switch':
13088                         this.showMode(1);
13089                         break;
13090                     case 'prev':
13091                     case 'next':
13092                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
13093                         switch(this.viewMode){
13094                                 case 0:
13095                                         this.viewDate = this.moveMonth(this.viewDate, dir);
13096                                         break;
13097                                 case 1:
13098                                 case 2:
13099                                         this.viewDate = this.moveYear(this.viewDate, dir);
13100                                         break;
13101                         }
13102                         this.fill();
13103                         break;
13104                     case 'today':
13105                         var date = new Date();
13106                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
13107                         this.fill()
13108                         this.setValue(this.formatDate(this.date));
13109                         this.hide();
13110                         break;
13111                 }
13112                 break;
13113             case 'span':
13114                 if (className.indexOf('disabled') === -1) {
13115                     this.viewDate.setUTCDate(1);
13116                     if (className.indexOf('month') !== -1) {
13117                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
13118                     } else {
13119                         var year = parseInt(html, 10) || 0;
13120                         this.viewDate.setUTCFullYear(year);
13121                         
13122                     }
13123                     this.showMode(-1);
13124                     this.fill();
13125                 }
13126                 break;
13127                 
13128             case 'td':
13129                 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
13130                     var day = parseInt(html, 10) || 1;
13131                     var year = this.viewDate.getUTCFullYear(),
13132                         month = this.viewDate.getUTCMonth();
13133
13134                     if (className.indexOf('old') !== -1) {
13135                         if(month === 0 ){
13136                             month = 11;
13137                             year -= 1;
13138                         }else{
13139                             month -= 1;
13140                         }
13141                     } else if (className.indexOf('new') !== -1) {
13142                         if (month == 11) {
13143                             month = 0;
13144                             year += 1;
13145                         } else {
13146                             month += 1;
13147                         }
13148                     }
13149                     this.date = this.UTCDate(year, month, day,0,0,0,0);
13150                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
13151                     this.fill();
13152                     this.setValue(this.formatDate(this.date));
13153                     this.hide();
13154                 }
13155                 break;
13156         }
13157     },
13158     
13159     setStartDate: function(startDate){
13160         this.startDate = startDate || -Infinity;
13161         if (this.startDate !== -Infinity) {
13162             this.startDate = this.parseDate(this.startDate);
13163         }
13164         this.update();
13165         this.updateNavArrows();
13166     },
13167
13168     setEndDate: function(endDate){
13169         this.endDate = endDate || Infinity;
13170         if (this.endDate !== Infinity) {
13171             this.endDate = this.parseDate(this.endDate);
13172         }
13173         this.update();
13174         this.updateNavArrows();
13175     },
13176     
13177     setDaysOfWeekDisabled: function(daysOfWeekDisabled){
13178         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
13179         if (typeof(this.daysOfWeekDisabled) !== 'object') {
13180             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
13181         }
13182         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
13183             return parseInt(d, 10);
13184         });
13185         this.update();
13186         this.updateNavArrows();
13187     },
13188     
13189     updateNavArrows: function() {
13190         var d = new Date(this.viewDate),
13191         year = d.getUTCFullYear(),
13192         month = d.getUTCMonth();
13193         
13194         Roo.each(this.picker().select('.prev', true).elements, function(v){
13195             v.show();
13196             switch (this.viewMode) {
13197                 case 0:
13198
13199                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
13200                         v.hide();
13201                     }
13202                     break;
13203                 case 1:
13204                 case 2:
13205                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
13206                         v.hide();
13207                     }
13208                     break;
13209             }
13210         });
13211         
13212         Roo.each(this.picker().select('.next', true).elements, function(v){
13213             v.show();
13214             switch (this.viewMode) {
13215                 case 0:
13216
13217                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
13218                         v.hide();
13219                     }
13220                     break;
13221                 case 1:
13222                 case 2:
13223                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
13224                         v.hide();
13225                     }
13226                     break;
13227             }
13228         })
13229     },
13230     
13231     moveMonth: function(date, dir){
13232         if (!dir) return date;
13233         var new_date = new Date(date.valueOf()),
13234         day = new_date.getUTCDate(),
13235         month = new_date.getUTCMonth(),
13236         mag = Math.abs(dir),
13237         new_month, test;
13238         dir = dir > 0 ? 1 : -1;
13239         if (mag == 1){
13240             test = dir == -1
13241             // If going back one month, make sure month is not current month
13242             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
13243             ? function(){
13244                 return new_date.getUTCMonth() == month;
13245             }
13246             // If going forward one month, make sure month is as expected
13247             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
13248             : function(){
13249                 return new_date.getUTCMonth() != new_month;
13250             };
13251             new_month = month + dir;
13252             new_date.setUTCMonth(new_month);
13253             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
13254             if (new_month < 0 || new_month > 11)
13255                 new_month = (new_month + 12) % 12;
13256         } else {
13257             // For magnitudes >1, move one month at a time...
13258             for (var i=0; i<mag; i++)
13259                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
13260                 new_date = this.moveMonth(new_date, dir);
13261             // ...then reset the day, keeping it in the new month
13262             new_month = new_date.getUTCMonth();
13263             new_date.setUTCDate(day);
13264             test = function(){
13265                 return new_month != new_date.getUTCMonth();
13266             };
13267         }
13268         // Common date-resetting loop -- if date is beyond end of month, make it
13269         // end of month
13270         while (test()){
13271             new_date.setUTCDate(--day);
13272             new_date.setUTCMonth(new_month);
13273         }
13274         return new_date;
13275     },
13276
13277     moveYear: function(date, dir){
13278         return this.moveMonth(date, dir*12);
13279     },
13280
13281     dateWithinRange: function(date){
13282         return date >= this.startDate && date <= this.endDate;
13283     },
13284
13285     
13286     remove: function() {
13287         this.picker().remove();
13288     }
13289    
13290 });
13291
13292 Roo.apply(Roo.bootstrap.DateField,  {
13293     
13294     head : {
13295         tag: 'thead',
13296         cn: [
13297         {
13298             tag: 'tr',
13299             cn: [
13300             {
13301                 tag: 'th',
13302                 cls: 'prev',
13303                 html: '<i class="icon-arrow-left"/>'
13304             },
13305             {
13306                 tag: 'th',
13307                 cls: 'switch',
13308                 colspan: '5'
13309             },
13310             {
13311                 tag: 'th',
13312                 cls: 'next',
13313                 html: '<i class="icon-arrow-right"/>'
13314             }
13315
13316             ]
13317         }
13318         ]
13319     },
13320     
13321     content : {
13322         tag: 'tbody',
13323         cn: [
13324         {
13325             tag: 'tr',
13326             cn: [
13327             {
13328                 tag: 'td',
13329                 colspan: '7'
13330             }
13331             ]
13332         }
13333         ]
13334     },
13335     
13336     footer : {
13337         tag: 'tfoot',
13338         cn: [
13339         {
13340             tag: 'tr',
13341             cn: [
13342             {
13343                 tag: 'th',
13344                 colspan: '7',
13345                 cls: 'today'
13346             }
13347                     
13348             ]
13349         }
13350         ]
13351     },
13352     
13353     dates:{
13354         en: {
13355             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
13356             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
13357             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
13358             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
13359             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
13360             today: "Today"
13361         }
13362     },
13363     
13364     modes: [
13365     {
13366         clsName: 'days',
13367         navFnc: 'Month',
13368         navStep: 1
13369     },
13370     {
13371         clsName: 'months',
13372         navFnc: 'FullYear',
13373         navStep: 1
13374     },
13375     {
13376         clsName: 'years',
13377         navFnc: 'FullYear',
13378         navStep: 10
13379     }]
13380 });
13381
13382 Roo.apply(Roo.bootstrap.DateField,  {
13383   
13384     template : {
13385         tag: 'div',
13386         cls: 'datepicker dropdown-menu',
13387         cn: [
13388         {
13389             tag: 'div',
13390             cls: 'datepicker-days',
13391             cn: [
13392             {
13393                 tag: 'table',
13394                 cls: 'table-condensed',
13395                 cn:[
13396                 Roo.bootstrap.DateField.head,
13397                 {
13398                     tag: 'tbody'
13399                 },
13400                 Roo.bootstrap.DateField.footer
13401                 ]
13402             }
13403             ]
13404         },
13405         {
13406             tag: 'div',
13407             cls: 'datepicker-months',
13408             cn: [
13409             {
13410                 tag: 'table',
13411                 cls: 'table-condensed',
13412                 cn:[
13413                 Roo.bootstrap.DateField.head,
13414                 Roo.bootstrap.DateField.content,
13415                 Roo.bootstrap.DateField.footer
13416                 ]
13417             }
13418             ]
13419         },
13420         {
13421             tag: 'div',
13422             cls: 'datepicker-years',
13423             cn: [
13424             {
13425                 tag: 'table',
13426                 cls: 'table-condensed',
13427                 cn:[
13428                 Roo.bootstrap.DateField.head,
13429                 Roo.bootstrap.DateField.content,
13430                 Roo.bootstrap.DateField.footer
13431                 ]
13432             }
13433             ]
13434         }
13435         ]
13436     }
13437 });
13438
13439  
13440
13441  /*
13442  * - LGPL
13443  *
13444  * TimeField
13445  * 
13446  */
13447
13448 /**
13449  * @class Roo.bootstrap.TimeField
13450  * @extends Roo.bootstrap.Input
13451  * Bootstrap DateField class
13452  * 
13453  * 
13454  * @constructor
13455  * Create a new TimeField
13456  * @param {Object} config The config object
13457  */
13458
13459 Roo.bootstrap.TimeField = function(config){
13460     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
13461     this.addEvents({
13462             /**
13463              * @event show
13464              * Fires when this field show.
13465              * @param {Roo.bootstrap.DateField} this
13466              * @param {Mixed} date The date value
13467              */
13468             show : true,
13469             /**
13470              * @event show
13471              * Fires when this field hide.
13472              * @param {Roo.bootstrap.DateField} this
13473              * @param {Mixed} date The date value
13474              */
13475             hide : true,
13476             /**
13477              * @event select
13478              * Fires when select a date.
13479              * @param {Roo.bootstrap.DateField} this
13480              * @param {Mixed} date The date value
13481              */
13482             select : true
13483         });
13484 };
13485
13486 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
13487     
13488     /**
13489      * @cfg {String} format
13490      * The default time format string which can be overriden for localization support.  The format must be
13491      * valid according to {@link Date#parseDate} (defaults to 'H:i').
13492      */
13493     format : "H:i",
13494        
13495     onRender: function(ct, position)
13496     {
13497         
13498         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
13499                 
13500         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
13501         
13502         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13503         
13504         this.pop = this.picker().select('>.datepicker-time',true).first();
13505         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block' 
13506         
13507         this.picker().on('mousedown', this.onMousedown, this);
13508         this.picker().on('click', this.onClick, this);
13509         
13510         this.picker().addClass('datepicker-dropdown');
13511     
13512         this.fillTime();
13513         this.update();
13514             
13515         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
13516         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
13517         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
13518         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
13519         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
13520         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
13521
13522     },
13523     
13524     fireKey: function(e){
13525         if (!this.picker().isVisible()){
13526             if (e.keyCode == 27) // allow escape to hide and re-show picker
13527                 this.show();
13528             return;
13529         }
13530
13531         e.preventDefault();
13532         
13533         switch(e.keyCode){
13534             case 27: // escape
13535                 this.hide();
13536                 break;
13537             case 37: // left
13538             case 39: // right
13539                 this.onTogglePeriod();
13540                 break;
13541             case 38: // up
13542                 this.onIncrementMinutes();
13543                 break;
13544             case 40: // down
13545                 this.onDecrementMinutes();
13546                 break;
13547             case 13: // enter
13548             case 9: // tab
13549                 this.setTime();
13550                 break;
13551         }
13552     },
13553     
13554     onClick: function(e) {
13555         e.stopPropagation();
13556         e.preventDefault();
13557     },
13558     
13559     picker : function()
13560     {
13561         return this.el.select('.datepicker', true).first();
13562     },
13563     
13564     fillTime: function()
13565     {    
13566         var time = this.pop.select('tbody', true).first();
13567         
13568         time.dom.innerHTML = '';
13569         
13570         time.createChild({
13571             tag: 'tr',
13572             cn: [
13573                 {
13574                     tag: 'td',
13575                     cn: [
13576                         {
13577                             tag: 'a',
13578                             href: '#',
13579                             cls: 'btn',
13580                             cn: [
13581                                 {
13582                                     tag: 'span',
13583                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
13584                                 }
13585                             ]
13586                         } 
13587                     ]
13588                 },
13589                 {
13590                     tag: 'td',
13591                     cls: 'separator'
13592                 },
13593                 {
13594                     tag: 'td',
13595                     cn: [
13596                         {
13597                             tag: 'a',
13598                             href: '#',
13599                             cls: 'btn',
13600                             cn: [
13601                                 {
13602                                     tag: 'span',
13603                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
13604                                 }
13605                             ]
13606                         }
13607                     ]
13608                 },
13609                 {
13610                     tag: 'td',
13611                     cls: 'separator'
13612                 }
13613             ]
13614         });
13615         
13616         time.createChild({
13617             tag: 'tr',
13618             cn: [
13619                 {
13620                     tag: 'td',
13621                     cn: [
13622                         {
13623                             tag: 'span',
13624                             cls: 'timepicker-hour',
13625                             html: '00'
13626                         }  
13627                     ]
13628                 },
13629                 {
13630                     tag: 'td',
13631                     cls: 'separator',
13632                     html: ':'
13633                 },
13634                 {
13635                     tag: 'td',
13636                     cn: [
13637                         {
13638                             tag: 'span',
13639                             cls: 'timepicker-minute',
13640                             html: '00'
13641                         }  
13642                     ]
13643                 },
13644                 {
13645                     tag: 'td',
13646                     cls: 'separator'
13647                 },
13648                 {
13649                     tag: 'td',
13650                     cn: [
13651                         {
13652                             tag: 'button',
13653                             type: 'button',
13654                             cls: 'btn btn-primary period',
13655                             html: 'AM'
13656                             
13657                         }
13658                     ]
13659                 }
13660             ]
13661         });
13662         
13663         time.createChild({
13664             tag: 'tr',
13665             cn: [
13666                 {
13667                     tag: 'td',
13668                     cn: [
13669                         {
13670                             tag: 'a',
13671                             href: '#',
13672                             cls: 'btn',
13673                             cn: [
13674                                 {
13675                                     tag: 'span',
13676                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
13677                                 }
13678                             ]
13679                         }
13680                     ]
13681                 },
13682                 {
13683                     tag: 'td',
13684                     cls: 'separator'
13685                 },
13686                 {
13687                     tag: 'td',
13688                     cn: [
13689                         {
13690                             tag: 'a',
13691                             href: '#',
13692                             cls: 'btn',
13693                             cn: [
13694                                 {
13695                                     tag: 'span',
13696                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
13697                                 }
13698                             ]
13699                         }
13700                     ]
13701                 },
13702                 {
13703                     tag: 'td',
13704                     cls: 'separator'
13705                 }
13706             ]
13707         });
13708         
13709     },
13710     
13711     update: function()
13712     {
13713         
13714         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
13715         
13716         this.fill();
13717     },
13718     
13719     fill: function() 
13720     {
13721         var hours = this.time.getHours();
13722         var minutes = this.time.getMinutes();
13723         var period = 'AM';
13724         
13725         if(hours > 11){
13726             period = 'PM';
13727         }
13728         
13729         if(hours == 0){
13730             hours = 12;
13731         }
13732         
13733         
13734         if(hours > 12){
13735             hours = hours - 12;
13736         }
13737         
13738         if(hours < 10){
13739             hours = '0' + hours;
13740         }
13741         
13742         if(minutes < 10){
13743             minutes = '0' + minutes;
13744         }
13745         
13746         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
13747         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
13748         this.pop.select('button', true).first().dom.innerHTML = period;
13749         
13750     },
13751     
13752     place: function()
13753     {   
13754         this.picker().removeClass(['bottom', 'top']);
13755         
13756         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
13757             /*
13758              * place to the top of element!
13759              *
13760              */
13761             
13762             this.picker().addClass('top');
13763             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
13764             
13765             return;
13766         }
13767         
13768         this.picker().addClass('bottom');
13769         
13770         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
13771     },
13772   
13773     onFocus : function()
13774     {
13775         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
13776         this.show();
13777     },
13778     
13779     onBlur : function()
13780     {
13781         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
13782         this.hide();
13783     },
13784     
13785     show : function()
13786     {
13787         this.picker().show();
13788         this.pop.show();
13789         this.update();
13790         this.place();
13791         
13792         this.fireEvent('show', this, this.date);
13793     },
13794     
13795     hide : function()
13796     {
13797         this.picker().hide();
13798         this.pop.hide();
13799         
13800         this.fireEvent('hide', this, this.date);
13801     },
13802     
13803     setTime : function()
13804     {
13805         this.hide();
13806         this.setValue(this.time.format(this.format));
13807         
13808         this.fireEvent('select', this, this.date);
13809         
13810         
13811     },
13812     
13813     onMousedown: function(e){
13814         e.stopPropagation();
13815         e.preventDefault();
13816     },
13817     
13818     onIncrementHours: function()
13819     {
13820         Roo.log('onIncrementHours');
13821         this.time = this.time.add(Date.HOUR, 1);
13822         this.update();
13823         
13824     },
13825     
13826     onDecrementHours: function()
13827     {
13828         Roo.log('onDecrementHours');
13829         this.time = this.time.add(Date.HOUR, -1);
13830         this.update();
13831     },
13832     
13833     onIncrementMinutes: function()
13834     {
13835         Roo.log('onIncrementMinutes');
13836         this.time = this.time.add(Date.MINUTE, 1);
13837         this.update();
13838     },
13839     
13840     onDecrementMinutes: function()
13841     {
13842         Roo.log('onDecrementMinutes');
13843         this.time = this.time.add(Date.MINUTE, -1);
13844         this.update();
13845     },
13846     
13847     onTogglePeriod: function()
13848     {
13849         Roo.log('onTogglePeriod');
13850         this.time = this.time.add(Date.HOUR, 12);
13851         this.update();
13852     }
13853     
13854    
13855 });
13856
13857 Roo.apply(Roo.bootstrap.TimeField,  {
13858     
13859     content : {
13860         tag: 'tbody',
13861         cn: [
13862             {
13863                 tag: 'tr',
13864                 cn: [
13865                 {
13866                     tag: 'td',
13867                     colspan: '7'
13868                 }
13869                 ]
13870             }
13871         ]
13872     },
13873     
13874     footer : {
13875         tag: 'tfoot',
13876         cn: [
13877             {
13878                 tag: 'tr',
13879                 cn: [
13880                 {
13881                     tag: 'th',
13882                     colspan: '7',
13883                     cls: '',
13884                     cn: [
13885                         {
13886                             tag: 'button',
13887                             cls: 'btn btn-info ok',
13888                             html: 'OK'
13889                         }
13890                     ]
13891                 }
13892
13893                 ]
13894             }
13895         ]
13896     }
13897 });
13898
13899 Roo.apply(Roo.bootstrap.TimeField,  {
13900   
13901     template : {
13902         tag: 'div',
13903         cls: 'datepicker dropdown-menu',
13904         cn: [
13905             {
13906                 tag: 'div',
13907                 cls: 'datepicker-time',
13908                 cn: [
13909                 {
13910                     tag: 'table',
13911                     cls: 'table-condensed',
13912                     cn:[
13913                     Roo.bootstrap.TimeField.content,
13914                     Roo.bootstrap.TimeField.footer
13915                     ]
13916                 }
13917                 ]
13918             }
13919         ]
13920     }
13921 });
13922
13923  
13924
13925  /*
13926  * - LGPL
13927  *
13928  * CheckBox
13929  * 
13930  */
13931
13932 /**
13933  * @class Roo.bootstrap.CheckBox
13934  * @extends Roo.bootstrap.Input
13935  * Bootstrap CheckBox class
13936  * 
13937  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
13938  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
13939  * @cfg {String} boxLabel The text that appears beside the checkbox
13940  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
13941  * @cfg {Boolean} checked initnal the element
13942  * 
13943  * 
13944  * @constructor
13945  * Create a new CheckBox
13946  * @param {Object} config The config object
13947  */
13948
13949 Roo.bootstrap.CheckBox = function(config){
13950     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
13951    
13952         this.addEvents({
13953             /**
13954             * @event check
13955             * Fires when the element is checked or unchecked.
13956             * @param {Roo.bootstrap.CheckBox} this This input
13957             * @param {Boolean} checked The new checked value
13958             */
13959            check : true
13960         });
13961 };
13962
13963 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
13964     
13965     inputType: 'checkbox',
13966     inputValue: 1,
13967     valueOff: 0,
13968     boxLabel: false,
13969     checked: false,
13970     weight : false,
13971     
13972     getAutoCreate : function()
13973     {
13974         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
13975         
13976         var id = Roo.id();
13977         
13978         var cfg = {};
13979         
13980         cfg.cls = 'form-group checkbox' //input-group
13981         
13982         
13983         
13984         
13985         var input =  {
13986             tag: 'input',
13987             id : id,
13988             type : this.inputType,
13989             value : (!this.checked) ? this.valueOff : this.inputValue,
13990             cls : 'roo-checkbox', //'form-box',
13991             placeholder : this.placeholder || ''
13992             
13993         };
13994         
13995         if (this.weight) { // Validity check?
13996             cfg.cls += " checkbox-" + this.weight;
13997         }
13998         
13999         if (this.disabled) {
14000             input.disabled=true;
14001         }
14002         
14003         if(this.checked){
14004             input.checked = this.checked;
14005         }
14006         
14007         if (this.name) {
14008             input.name = this.name;
14009         }
14010         
14011         if (this.size) {
14012             input.cls += ' input-' + this.size;
14013         }
14014         
14015         var settings=this;
14016         ['xs','sm','md','lg'].map(function(size){
14017             if (settings[size]) {
14018                 cfg.cls += ' col-' + size + '-' + settings[size];
14019             }
14020         });
14021         
14022        
14023         
14024         var inputblock = input;
14025         
14026         
14027         
14028         
14029         if (this.before || this.after) {
14030             
14031             inputblock = {
14032                 cls : 'input-group',
14033                 cn :  [] 
14034             };
14035             if (this.before) {
14036                 inputblock.cn.push({
14037                     tag :'span',
14038                     cls : 'input-group-addon',
14039                     html : this.before
14040                 });
14041             }
14042             inputblock.cn.push(input);
14043             if (this.after) {
14044                 inputblock.cn.push({
14045                     tag :'span',
14046                     cls : 'input-group-addon',
14047                     html : this.after
14048                 });
14049             }
14050             
14051         };
14052         
14053         if (align ==='left' && this.fieldLabel.length) {
14054                 Roo.log("left and has label");
14055                 cfg.cn = [
14056                     
14057                     {
14058                         tag: 'label',
14059                         'for' :  id,
14060                         cls : 'control-label col-md-' + this.labelWidth,
14061                         html : this.fieldLabel
14062                         
14063                     },
14064                     {
14065                         cls : "col-md-" + (12 - this.labelWidth), 
14066                         cn: [
14067                             inputblock
14068                         ]
14069                     }
14070                     
14071                 ];
14072         } else if ( this.fieldLabel.length) {
14073                 Roo.log(" label");
14074                 cfg.cn = [
14075                    
14076                     {
14077                         tag: this.boxLabel ? 'span' : 'label',
14078                         'for': id,
14079                         cls: 'control-label box-input-label',
14080                         //cls : 'input-group-addon',
14081                         html : this.fieldLabel
14082                         
14083                     },
14084                     
14085                     inputblock
14086                     
14087                 ];
14088
14089         } else {
14090             
14091                 Roo.log(" no label && no align");
14092                 cfg.cn = [  inputblock ] ;
14093                 
14094                 
14095         };
14096          if(this.boxLabel){
14097             cfg.cn.push( {
14098                 tag: 'label',
14099                 'for': id,
14100                 cls: 'box-label',
14101                 html: this.boxLabel
14102                 
14103             });
14104         }
14105         
14106         
14107        
14108         return cfg;
14109         
14110     },
14111     
14112     /**
14113      * return the real input element.
14114      */
14115     inputEl: function ()
14116     {
14117         return this.el.select('input.roo-checkbox',true).first();
14118     },
14119     
14120     label: function()
14121     {
14122         return this.el.select('label.control-label',true).first();
14123     },
14124     
14125     initEvents : function()
14126     {
14127 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
14128         
14129         this.inputEl().on('click', this.onClick,  this);
14130         
14131     },
14132     
14133     onClick : function()
14134     {   
14135         this.setChecked(!this.checked);
14136     },
14137     
14138     setChecked : function(state,suppressEvent)
14139     {
14140         this.checked = state;
14141         
14142         this.inputEl().dom.checked = state;
14143         
14144         if(suppressEvent !== true){
14145             this.fireEvent('check', this, state);
14146         }
14147         
14148         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
14149         
14150     },
14151     
14152     setValue : function(v,suppressEvent)
14153     {
14154         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
14155     }
14156     
14157 });
14158
14159  
14160 /*
14161  * - LGPL
14162  *
14163  * Radio
14164  * 
14165  */
14166
14167 /**
14168  * @class Roo.bootstrap.Radio
14169  * @extends Roo.bootstrap.CheckBox
14170  * Bootstrap Radio class
14171
14172  * @constructor
14173  * Create a new Radio
14174  * @param {Object} config The config object
14175  */
14176
14177 Roo.bootstrap.Radio = function(config){
14178     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
14179    
14180 };
14181
14182 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
14183     
14184     inputType: 'radio',
14185     inputValue: '',
14186     valueOff: '',
14187     
14188     getAutoCreate : function()
14189     {
14190         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
14191         
14192         var id = Roo.id();
14193         
14194         var cfg = {};
14195         
14196         cfg.cls = 'form-group radio' //input-group
14197         
14198         var input =  {
14199             tag: 'input',
14200             id : id,
14201             type : this.inputType,
14202             value : (!this.checked) ? this.valueOff : this.inputValue,
14203             cls : 'roo-radio',
14204             placeholder : this.placeholder || ''
14205             
14206         };
14207           if (this.weight) { // Validity check?
14208             cfg.cls += " radio-" + this.weight;
14209         }
14210         if (this.disabled) {
14211             input.disabled=true;
14212         }
14213         
14214         if(this.checked){
14215             input.checked = this.checked;
14216         }
14217         
14218         if (this.name) {
14219             input.name = this.name;
14220         }
14221         
14222         if (this.size) {
14223             input.cls += ' input-' + this.size;
14224         }
14225         
14226         var settings=this;
14227         ['xs','sm','md','lg'].map(function(size){
14228             if (settings[size]) {
14229                 cfg.cls += ' col-' + size + '-' + settings[size];
14230             }
14231         });
14232         
14233         var inputblock = input;
14234         
14235         if (this.before || this.after) {
14236             
14237             inputblock = {
14238                 cls : 'input-group',
14239                 cn :  [] 
14240             };
14241             if (this.before) {
14242                 inputblock.cn.push({
14243                     tag :'span',
14244                     cls : 'input-group-addon',
14245                     html : this.before
14246                 });
14247             }
14248             inputblock.cn.push(input);
14249             if (this.after) {
14250                 inputblock.cn.push({
14251                     tag :'span',
14252                     cls : 'input-group-addon',
14253                     html : this.after
14254                 });
14255             }
14256             
14257         };
14258         
14259         if (align ==='left' && this.fieldLabel.length) {
14260                 Roo.log("left and has label");
14261                 cfg.cn = [
14262                     
14263                     {
14264                         tag: 'label',
14265                         'for' :  id,
14266                         cls : 'control-label col-md-' + this.labelWidth,
14267                         html : this.fieldLabel
14268                         
14269                     },
14270                     {
14271                         cls : "col-md-" + (12 - this.labelWidth), 
14272                         cn: [
14273                             inputblock
14274                         ]
14275                     }
14276                     
14277                 ];
14278         } else if ( this.fieldLabel.length) {
14279                 Roo.log(" label");
14280                  cfg.cn = [
14281                    
14282                     {
14283                         tag: 'label',
14284                         'for': id,
14285                         cls: 'control-label box-input-label',
14286                         //cls : 'input-group-addon',
14287                         html : this.fieldLabel
14288                         
14289                     },
14290                     
14291                     inputblock
14292                     
14293                 ];
14294
14295         } else {
14296             
14297                    Roo.log(" no label && no align");
14298                 cfg.cn = [
14299                     
14300                         inputblock
14301                     
14302                 ];
14303                 
14304                 
14305         };
14306         
14307         if(this.boxLabel){
14308             cfg.cn.push({
14309                 tag: 'label',
14310                 'for': id,
14311                 cls: 'box-label',
14312                 html: this.boxLabel
14313             })
14314         }
14315         
14316         return cfg;
14317         
14318     },
14319     inputEl: function ()
14320     {
14321         return this.el.select('input.roo-radio',true).first();
14322     },
14323     onClick : function()
14324     {   
14325         this.setChecked(true);
14326     },
14327     
14328     setChecked : function(state,suppressEvent)
14329     {
14330         if(state){
14331             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
14332                 v.dom.checked = false;
14333             });
14334         }
14335         
14336         this.checked = state;
14337         this.inputEl().dom.checked = state;
14338         
14339         if(suppressEvent !== true){
14340             this.fireEvent('check', this, state);
14341         }
14342         
14343         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
14344         
14345     },
14346     
14347     getGroupValue : function()
14348     {
14349         var value = ''
14350         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
14351             if(v.dom.checked == true){
14352                 value = v.dom.value;
14353             }
14354         });
14355         
14356         return value;
14357     },
14358     
14359     /**
14360      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
14361      * @return {Mixed} value The field value
14362      */
14363     getValue : function(){
14364         return this.getGroupValue();
14365     }
14366     
14367 });
14368
14369  
14370 //<script type="text/javascript">
14371
14372 /*
14373  * Based  Ext JS Library 1.1.1
14374  * Copyright(c) 2006-2007, Ext JS, LLC.
14375  * LGPL
14376  *
14377  */
14378  
14379 /**
14380  * @class Roo.HtmlEditorCore
14381  * @extends Roo.Component
14382  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
14383  *
14384  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
14385  */
14386
14387 Roo.HtmlEditorCore = function(config){
14388     
14389     
14390     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
14391     this.addEvents({
14392         /**
14393          * @event initialize
14394          * Fires when the editor is fully initialized (including the iframe)
14395          * @param {Roo.HtmlEditorCore} this
14396          */
14397         initialize: true,
14398         /**
14399          * @event activate
14400          * Fires when the editor is first receives the focus. Any insertion must wait
14401          * until after this event.
14402          * @param {Roo.HtmlEditorCore} this
14403          */
14404         activate: true,
14405          /**
14406          * @event beforesync
14407          * Fires before the textarea is updated with content from the editor iframe. Return false
14408          * to cancel the sync.
14409          * @param {Roo.HtmlEditorCore} this
14410          * @param {String} html
14411          */
14412         beforesync: true,
14413          /**
14414          * @event beforepush
14415          * Fires before the iframe editor is updated with content from the textarea. Return false
14416          * to cancel the push.
14417          * @param {Roo.HtmlEditorCore} this
14418          * @param {String} html
14419          */
14420         beforepush: true,
14421          /**
14422          * @event sync
14423          * Fires when the textarea is updated with content from the editor iframe.
14424          * @param {Roo.HtmlEditorCore} this
14425          * @param {String} html
14426          */
14427         sync: true,
14428          /**
14429          * @event push
14430          * Fires when the iframe editor is updated with content from the textarea.
14431          * @param {Roo.HtmlEditorCore} this
14432          * @param {String} html
14433          */
14434         push: true,
14435         
14436         /**
14437          * @event editorevent
14438          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
14439          * @param {Roo.HtmlEditorCore} this
14440          */
14441         editorevent: true
14442     });
14443      
14444 };
14445
14446
14447 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
14448
14449
14450      /**
14451      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
14452      */
14453     
14454     owner : false,
14455     
14456      /**
14457      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
14458      *                        Roo.resizable.
14459      */
14460     resizable : false,
14461      /**
14462      * @cfg {Number} height (in pixels)
14463      */   
14464     height: 300,
14465    /**
14466      * @cfg {Number} width (in pixels)
14467      */   
14468     width: 500,
14469     
14470     /**
14471      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
14472      * 
14473      */
14474     stylesheets: false,
14475     
14476     // id of frame..
14477     frameId: false,
14478     
14479     // private properties
14480     validationEvent : false,
14481     deferHeight: true,
14482     initialized : false,
14483     activated : false,
14484     sourceEditMode : false,
14485     onFocus : Roo.emptyFn,
14486     iframePad:3,
14487     hideMode:'offsets',
14488     
14489     clearUp: true,
14490     
14491      
14492     
14493
14494     /**
14495      * Protected method that will not generally be called directly. It
14496      * is called when the editor initializes the iframe with HTML contents. Override this method if you
14497      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
14498      */
14499     getDocMarkup : function(){
14500         // body styles..
14501         var st = '';
14502         Roo.log(this.stylesheets);
14503         
14504         // inherit styels from page...?? 
14505         if (this.stylesheets === false) {
14506             
14507             Roo.get(document.head).select('style').each(function(node) {
14508                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
14509             });
14510             
14511             Roo.get(document.head).select('link').each(function(node) { 
14512                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
14513             });
14514             
14515         } else if (!this.stylesheets.length) {
14516                 // simple..
14517                 st = '<style type="text/css">' +
14518                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
14519                    '</style>';
14520         } else {
14521             Roo.each(this.stylesheets, function(s) {
14522                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
14523             });
14524             
14525         }
14526         
14527         st +=  '<style type="text/css">' +
14528             'IMG { cursor: pointer } ' +
14529         '</style>';
14530
14531         
14532         return '<html><head>' + st  +
14533             //<style type="text/css">' +
14534             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
14535             //'</style>' +
14536             ' </head><body class="roo-htmleditor-body"></body></html>';
14537     },
14538
14539     // private
14540     onRender : function(ct, position)
14541     {
14542         var _t = this;
14543         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
14544         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
14545         
14546         
14547         this.el.dom.style.border = '0 none';
14548         this.el.dom.setAttribute('tabIndex', -1);
14549         this.el.addClass('x-hidden hide');
14550         
14551         
14552         
14553         if(Roo.isIE){ // fix IE 1px bogus margin
14554             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
14555         }
14556        
14557         
14558         this.frameId = Roo.id();
14559         
14560          
14561         
14562         var iframe = this.owner.wrap.createChild({
14563             tag: 'iframe',
14564             cls: 'form-control', // bootstrap..
14565             id: this.frameId,
14566             name: this.frameId,
14567             frameBorder : 'no',
14568             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
14569         }, this.el
14570         );
14571         
14572         
14573         this.iframe = iframe.dom;
14574
14575          this.assignDocWin();
14576         
14577         this.doc.designMode = 'on';
14578        
14579         this.doc.open();
14580         this.doc.write(this.getDocMarkup());
14581         this.doc.close();
14582
14583         
14584         var task = { // must defer to wait for browser to be ready
14585             run : function(){
14586                 //console.log("run task?" + this.doc.readyState);
14587                 this.assignDocWin();
14588                 if(this.doc.body || this.doc.readyState == 'complete'){
14589                     try {
14590                         this.doc.designMode="on";
14591                     } catch (e) {
14592                         return;
14593                     }
14594                     Roo.TaskMgr.stop(task);
14595                     this.initEditor.defer(10, this);
14596                 }
14597             },
14598             interval : 10,
14599             duration: 10000,
14600             scope: this
14601         };
14602         Roo.TaskMgr.start(task);
14603
14604         
14605          
14606     },
14607
14608     // private
14609     onResize : function(w, h)
14610     {
14611          Roo.log('resize: ' +w + ',' + h );
14612         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
14613         if(!this.iframe){
14614             return;
14615         }
14616         if(typeof w == 'number'){
14617             
14618             this.iframe.style.width = w + 'px';
14619         }
14620         if(typeof h == 'number'){
14621             
14622             this.iframe.style.height = h + 'px';
14623             if(this.doc){
14624                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
14625             }
14626         }
14627         
14628     },
14629
14630     /**
14631      * Toggles the editor between standard and source edit mode.
14632      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
14633      */
14634     toggleSourceEdit : function(sourceEditMode){
14635         
14636         this.sourceEditMode = sourceEditMode === true;
14637         
14638         if(this.sourceEditMode){
14639  
14640             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
14641             
14642         }else{
14643             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
14644             //this.iframe.className = '';
14645             this.deferFocus();
14646         }
14647         //this.setSize(this.owner.wrap.getSize());
14648         //this.fireEvent('editmodechange', this, this.sourceEditMode);
14649     },
14650
14651     
14652   
14653
14654     /**
14655      * Protected method that will not generally be called directly. If you need/want
14656      * custom HTML cleanup, this is the method you should override.
14657      * @param {String} html The HTML to be cleaned
14658      * return {String} The cleaned HTML
14659      */
14660     cleanHtml : function(html){
14661         html = String(html);
14662         if(html.length > 5){
14663             if(Roo.isSafari){ // strip safari nonsense
14664                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
14665             }
14666         }
14667         if(html == '&nbsp;'){
14668             html = '';
14669         }
14670         return html;
14671     },
14672
14673     /**
14674      * HTML Editor -> Textarea
14675      * Protected method that will not generally be called directly. Syncs the contents
14676      * of the editor iframe with the textarea.
14677      */
14678     syncValue : function(){
14679         if(this.initialized){
14680             var bd = (this.doc.body || this.doc.documentElement);
14681             //this.cleanUpPaste(); -- this is done else where and causes havoc..
14682             var html = bd.innerHTML;
14683             if(Roo.isSafari){
14684                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
14685                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
14686                 if(m && m[1]){
14687                     html = '<div style="'+m[0]+'">' + html + '</div>';
14688                 }
14689             }
14690             html = this.cleanHtml(html);
14691             // fix up the special chars.. normaly like back quotes in word...
14692             // however we do not want to do this with chinese..
14693             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
14694                 var cc = b.charCodeAt();
14695                 if (
14696                     (cc >= 0x4E00 && cc < 0xA000 ) ||
14697                     (cc >= 0x3400 && cc < 0x4E00 ) ||
14698                     (cc >= 0xf900 && cc < 0xfb00 )
14699                 ) {
14700                         return b;
14701                 }
14702                 return "&#"+cc+";" 
14703             });
14704             if(this.owner.fireEvent('beforesync', this, html) !== false){
14705                 this.el.dom.value = html;
14706                 this.owner.fireEvent('sync', this, html);
14707             }
14708         }
14709     },
14710
14711     /**
14712      * Protected method that will not generally be called directly. Pushes the value of the textarea
14713      * into the iframe editor.
14714      */
14715     pushValue : function(){
14716         if(this.initialized){
14717             var v = this.el.dom.value.trim();
14718             
14719 //            if(v.length < 1){
14720 //                v = '&#160;';
14721 //            }
14722             
14723             if(this.owner.fireEvent('beforepush', this, v) !== false){
14724                 var d = (this.doc.body || this.doc.documentElement);
14725                 d.innerHTML = v;
14726                 this.cleanUpPaste();
14727                 this.el.dom.value = d.innerHTML;
14728                 this.owner.fireEvent('push', this, v);
14729             }
14730         }
14731     },
14732
14733     // private
14734     deferFocus : function(){
14735         this.focus.defer(10, this);
14736     },
14737
14738     // doc'ed in Field
14739     focus : function(){
14740         if(this.win && !this.sourceEditMode){
14741             this.win.focus();
14742         }else{
14743             this.el.focus();
14744         }
14745     },
14746     
14747     assignDocWin: function()
14748     {
14749         var iframe = this.iframe;
14750         
14751          if(Roo.isIE){
14752             this.doc = iframe.contentWindow.document;
14753             this.win = iframe.contentWindow;
14754         } else {
14755             if (!Roo.get(this.frameId)) {
14756                 return;
14757             }
14758             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
14759             this.win = Roo.get(this.frameId).dom.contentWindow;
14760         }
14761     },
14762     
14763     // private
14764     initEditor : function(){
14765         //console.log("INIT EDITOR");
14766         this.assignDocWin();
14767         
14768         
14769         
14770         this.doc.designMode="on";
14771         this.doc.open();
14772         this.doc.write(this.getDocMarkup());
14773         this.doc.close();
14774         
14775         var dbody = (this.doc.body || this.doc.documentElement);
14776         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
14777         // this copies styles from the containing element into thsi one..
14778         // not sure why we need all of this..
14779         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
14780         ss['background-attachment'] = 'fixed'; // w3c
14781         dbody.bgProperties = 'fixed'; // ie
14782         Roo.DomHelper.applyStyles(dbody, ss);
14783         Roo.EventManager.on(this.doc, {
14784             //'mousedown': this.onEditorEvent,
14785             'mouseup': this.onEditorEvent,
14786             'dblclick': this.onEditorEvent,
14787             'click': this.onEditorEvent,
14788             'keyup': this.onEditorEvent,
14789             buffer:100,
14790             scope: this
14791         });
14792         if(Roo.isGecko){
14793             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
14794         }
14795         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
14796             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
14797         }
14798         this.initialized = true;
14799
14800         this.owner.fireEvent('initialize', this);
14801         this.pushValue();
14802     },
14803
14804     // private
14805     onDestroy : function(){
14806         
14807         
14808         
14809         if(this.rendered){
14810             
14811             //for (var i =0; i < this.toolbars.length;i++) {
14812             //    // fixme - ask toolbars for heights?
14813             //    this.toolbars[i].onDestroy();
14814            // }
14815             
14816             //this.wrap.dom.innerHTML = '';
14817             //this.wrap.remove();
14818         }
14819     },
14820
14821     // private
14822     onFirstFocus : function(){
14823         
14824         this.assignDocWin();
14825         
14826         
14827         this.activated = true;
14828          
14829     
14830         if(Roo.isGecko){ // prevent silly gecko errors
14831             this.win.focus();
14832             var s = this.win.getSelection();
14833             if(!s.focusNode || s.focusNode.nodeType != 3){
14834                 var r = s.getRangeAt(0);
14835                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
14836                 r.collapse(true);
14837                 this.deferFocus();
14838             }
14839             try{
14840                 this.execCmd('useCSS', true);
14841                 this.execCmd('styleWithCSS', false);
14842             }catch(e){}
14843         }
14844         this.owner.fireEvent('activate', this);
14845     },
14846
14847     // private
14848     adjustFont: function(btn){
14849         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
14850         //if(Roo.isSafari){ // safari
14851         //    adjust *= 2;
14852        // }
14853         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
14854         if(Roo.isSafari){ // safari
14855             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
14856             v =  (v < 10) ? 10 : v;
14857             v =  (v > 48) ? 48 : v;
14858             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
14859             
14860         }
14861         
14862         
14863         v = Math.max(1, v+adjust);
14864         
14865         this.execCmd('FontSize', v  );
14866     },
14867
14868     onEditorEvent : function(e){
14869         this.owner.fireEvent('editorevent', this, e);
14870       //  this.updateToolbar();
14871         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
14872     },
14873
14874     insertTag : function(tg)
14875     {
14876         // could be a bit smarter... -> wrap the current selected tRoo..
14877         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
14878             
14879             range = this.createRange(this.getSelection());
14880             var wrappingNode = this.doc.createElement(tg.toLowerCase());
14881             wrappingNode.appendChild(range.extractContents());
14882             range.insertNode(wrappingNode);
14883
14884             return;
14885             
14886             
14887             
14888         }
14889         this.execCmd("formatblock",   tg);
14890         
14891     },
14892     
14893     insertText : function(txt)
14894     {
14895         
14896         
14897         var range = this.createRange();
14898         range.deleteContents();
14899                //alert(Sender.getAttribute('label'));
14900                
14901         range.insertNode(this.doc.createTextNode(txt));
14902     } ,
14903     
14904      
14905
14906     /**
14907      * Executes a Midas editor command on the editor document and performs necessary focus and
14908      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
14909      * @param {String} cmd The Midas command
14910      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
14911      */
14912     relayCmd : function(cmd, value){
14913         this.win.focus();
14914         this.execCmd(cmd, value);
14915         this.owner.fireEvent('editorevent', this);
14916         //this.updateToolbar();
14917         this.owner.deferFocus();
14918     },
14919
14920     /**
14921      * Executes a Midas editor command directly on the editor document.
14922      * For visual commands, you should use {@link #relayCmd} instead.
14923      * <b>This should only be called after the editor is initialized.</b>
14924      * @param {String} cmd The Midas command
14925      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
14926      */
14927     execCmd : function(cmd, value){
14928         this.doc.execCommand(cmd, false, value === undefined ? null : value);
14929         this.syncValue();
14930     },
14931  
14932  
14933    
14934     /**
14935      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
14936      * to insert tRoo.
14937      * @param {String} text | dom node.. 
14938      */
14939     insertAtCursor : function(text)
14940     {
14941         
14942         
14943         
14944         if(!this.activated){
14945             return;
14946         }
14947         /*
14948         if(Roo.isIE){
14949             this.win.focus();
14950             var r = this.doc.selection.createRange();
14951             if(r){
14952                 r.collapse(true);
14953                 r.pasteHTML(text);
14954                 this.syncValue();
14955                 this.deferFocus();
14956             
14957             }
14958             return;
14959         }
14960         */
14961         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
14962             this.win.focus();
14963             
14964             
14965             // from jquery ui (MIT licenced)
14966             var range, node;
14967             var win = this.win;
14968             
14969             if (win.getSelection && win.getSelection().getRangeAt) {
14970                 range = win.getSelection().getRangeAt(0);
14971                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
14972                 range.insertNode(node);
14973             } else if (win.document.selection && win.document.selection.createRange) {
14974                 // no firefox support
14975                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
14976                 win.document.selection.createRange().pasteHTML(txt);
14977             } else {
14978                 // no firefox support
14979                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
14980                 this.execCmd('InsertHTML', txt);
14981             } 
14982             
14983             this.syncValue();
14984             
14985             this.deferFocus();
14986         }
14987     },
14988  // private
14989     mozKeyPress : function(e){
14990         if(e.ctrlKey){
14991             var c = e.getCharCode(), cmd;
14992           
14993             if(c > 0){
14994                 c = String.fromCharCode(c).toLowerCase();
14995                 switch(c){
14996                     case 'b':
14997                         cmd = 'bold';
14998                         break;
14999                     case 'i':
15000                         cmd = 'italic';
15001                         break;
15002                     
15003                     case 'u':
15004                         cmd = 'underline';
15005                         break;
15006                     
15007                     case 'v':
15008                         this.cleanUpPaste.defer(100, this);
15009                         return;
15010                         
15011                 }
15012                 if(cmd){
15013                     this.win.focus();
15014                     this.execCmd(cmd);
15015                     this.deferFocus();
15016                     e.preventDefault();
15017                 }
15018                 
15019             }
15020         }
15021     },
15022
15023     // private
15024     fixKeys : function(){ // load time branching for fastest keydown performance
15025         if(Roo.isIE){
15026             return function(e){
15027                 var k = e.getKey(), r;
15028                 if(k == e.TAB){
15029                     e.stopEvent();
15030                     r = this.doc.selection.createRange();
15031                     if(r){
15032                         r.collapse(true);
15033                         r.pasteHTML('&#160;&#160;&#160;&#160;');
15034                         this.deferFocus();
15035                     }
15036                     return;
15037                 }
15038                 
15039                 if(k == e.ENTER){
15040                     r = this.doc.selection.createRange();
15041                     if(r){
15042                         var target = r.parentElement();
15043                         if(!target || target.tagName.toLowerCase() != 'li'){
15044                             e.stopEvent();
15045                             r.pasteHTML('<br />');
15046                             r.collapse(false);
15047                             r.select();
15048                         }
15049                     }
15050                 }
15051                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
15052                     this.cleanUpPaste.defer(100, this);
15053                     return;
15054                 }
15055                 
15056                 
15057             };
15058         }else if(Roo.isOpera){
15059             return function(e){
15060                 var k = e.getKey();
15061                 if(k == e.TAB){
15062                     e.stopEvent();
15063                     this.win.focus();
15064                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
15065                     this.deferFocus();
15066                 }
15067                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
15068                     this.cleanUpPaste.defer(100, this);
15069                     return;
15070                 }
15071                 
15072             };
15073         }else if(Roo.isSafari){
15074             return function(e){
15075                 var k = e.getKey();
15076                 
15077                 if(k == e.TAB){
15078                     e.stopEvent();
15079                     this.execCmd('InsertText','\t');
15080                     this.deferFocus();
15081                     return;
15082                 }
15083                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
15084                     this.cleanUpPaste.defer(100, this);
15085                     return;
15086                 }
15087                 
15088              };
15089         }
15090     }(),
15091     
15092     getAllAncestors: function()
15093     {
15094         var p = this.getSelectedNode();
15095         var a = [];
15096         if (!p) {
15097             a.push(p); // push blank onto stack..
15098             p = this.getParentElement();
15099         }
15100         
15101         
15102         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
15103             a.push(p);
15104             p = p.parentNode;
15105         }
15106         a.push(this.doc.body);
15107         return a;
15108     },
15109     lastSel : false,
15110     lastSelNode : false,
15111     
15112     
15113     getSelection : function() 
15114     {
15115         this.assignDocWin();
15116         return Roo.isIE ? this.doc.selection : this.win.getSelection();
15117     },
15118     
15119     getSelectedNode: function() 
15120     {
15121         // this may only work on Gecko!!!
15122         
15123         // should we cache this!!!!
15124         
15125         
15126         
15127          
15128         var range = this.createRange(this.getSelection()).cloneRange();
15129         
15130         if (Roo.isIE) {
15131             var parent = range.parentElement();
15132             while (true) {
15133                 var testRange = range.duplicate();
15134                 testRange.moveToElementText(parent);
15135                 if (testRange.inRange(range)) {
15136                     break;
15137                 }
15138                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
15139                     break;
15140                 }
15141                 parent = parent.parentElement;
15142             }
15143             return parent;
15144         }
15145         
15146         // is ancestor a text element.
15147         var ac =  range.commonAncestorContainer;
15148         if (ac.nodeType == 3) {
15149             ac = ac.parentNode;
15150         }
15151         
15152         var ar = ac.childNodes;
15153          
15154         var nodes = [];
15155         var other_nodes = [];
15156         var has_other_nodes = false;
15157         for (var i=0;i<ar.length;i++) {
15158             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
15159                 continue;
15160             }
15161             // fullly contained node.
15162             
15163             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
15164                 nodes.push(ar[i]);
15165                 continue;
15166             }
15167             
15168             // probably selected..
15169             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
15170                 other_nodes.push(ar[i]);
15171                 continue;
15172             }
15173             // outer..
15174             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
15175                 continue;
15176             }
15177             
15178             
15179             has_other_nodes = true;
15180         }
15181         if (!nodes.length && other_nodes.length) {
15182             nodes= other_nodes;
15183         }
15184         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
15185             return false;
15186         }
15187         
15188         return nodes[0];
15189     },
15190     createRange: function(sel)
15191     {
15192         // this has strange effects when using with 
15193         // top toolbar - not sure if it's a great idea.
15194         //this.editor.contentWindow.focus();
15195         if (typeof sel != "undefined") {
15196             try {
15197                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
15198             } catch(e) {
15199                 return this.doc.createRange();
15200             }
15201         } else {
15202             return this.doc.createRange();
15203         }
15204     },
15205     getParentElement: function()
15206     {
15207         
15208         this.assignDocWin();
15209         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
15210         
15211         var range = this.createRange(sel);
15212          
15213         try {
15214             var p = range.commonAncestorContainer;
15215             while (p.nodeType == 3) { // text node
15216                 p = p.parentNode;
15217             }
15218             return p;
15219         } catch (e) {
15220             return null;
15221         }
15222     
15223     },
15224     /***
15225      *
15226      * Range intersection.. the hard stuff...
15227      *  '-1' = before
15228      *  '0' = hits..
15229      *  '1' = after.
15230      *         [ -- selected range --- ]
15231      *   [fail]                        [fail]
15232      *
15233      *    basically..
15234      *      if end is before start or  hits it. fail.
15235      *      if start is after end or hits it fail.
15236      *
15237      *   if either hits (but other is outside. - then it's not 
15238      *   
15239      *    
15240      **/
15241     
15242     
15243     // @see http://www.thismuchiknow.co.uk/?p=64.
15244     rangeIntersectsNode : function(range, node)
15245     {
15246         var nodeRange = node.ownerDocument.createRange();
15247         try {
15248             nodeRange.selectNode(node);
15249         } catch (e) {
15250             nodeRange.selectNodeContents(node);
15251         }
15252     
15253         var rangeStartRange = range.cloneRange();
15254         rangeStartRange.collapse(true);
15255     
15256         var rangeEndRange = range.cloneRange();
15257         rangeEndRange.collapse(false);
15258     
15259         var nodeStartRange = nodeRange.cloneRange();
15260         nodeStartRange.collapse(true);
15261     
15262         var nodeEndRange = nodeRange.cloneRange();
15263         nodeEndRange.collapse(false);
15264     
15265         return rangeStartRange.compareBoundaryPoints(
15266                  Range.START_TO_START, nodeEndRange) == -1 &&
15267                rangeEndRange.compareBoundaryPoints(
15268                  Range.START_TO_START, nodeStartRange) == 1;
15269         
15270          
15271     },
15272     rangeCompareNode : function(range, node)
15273     {
15274         var nodeRange = node.ownerDocument.createRange();
15275         try {
15276             nodeRange.selectNode(node);
15277         } catch (e) {
15278             nodeRange.selectNodeContents(node);
15279         }
15280         
15281         
15282         range.collapse(true);
15283     
15284         nodeRange.collapse(true);
15285      
15286         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
15287         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
15288          
15289         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
15290         
15291         var nodeIsBefore   =  ss == 1;
15292         var nodeIsAfter    = ee == -1;
15293         
15294         if (nodeIsBefore && nodeIsAfter)
15295             return 0; // outer
15296         if (!nodeIsBefore && nodeIsAfter)
15297             return 1; //right trailed.
15298         
15299         if (nodeIsBefore && !nodeIsAfter)
15300             return 2;  // left trailed.
15301         // fully contined.
15302         return 3;
15303     },
15304
15305     // private? - in a new class?
15306     cleanUpPaste :  function()
15307     {
15308         // cleans up the whole document..
15309         Roo.log('cleanuppaste');
15310         
15311         this.cleanUpChildren(this.doc.body);
15312         var clean = this.cleanWordChars(this.doc.body.innerHTML);
15313         if (clean != this.doc.body.innerHTML) {
15314             this.doc.body.innerHTML = clean;
15315         }
15316         
15317     },
15318     
15319     cleanWordChars : function(input) {// change the chars to hex code
15320         var he = Roo.HtmlEditorCore;
15321         
15322         var output = input;
15323         Roo.each(he.swapCodes, function(sw) { 
15324             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
15325             
15326             output = output.replace(swapper, sw[1]);
15327         });
15328         
15329         return output;
15330     },
15331     
15332     
15333     cleanUpChildren : function (n)
15334     {
15335         if (!n.childNodes.length) {
15336             return;
15337         }
15338         for (var i = n.childNodes.length-1; i > -1 ; i--) {
15339            this.cleanUpChild(n.childNodes[i]);
15340         }
15341     },
15342     
15343     
15344         
15345     
15346     cleanUpChild : function (node)
15347     {
15348         var ed = this;
15349         //console.log(node);
15350         if (node.nodeName == "#text") {
15351             // clean up silly Windows -- stuff?
15352             return; 
15353         }
15354         if (node.nodeName == "#comment") {
15355             node.parentNode.removeChild(node);
15356             // clean up silly Windows -- stuff?
15357             return; 
15358         }
15359         
15360         if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1 && this.clearUp) {
15361             // remove node.
15362             node.parentNode.removeChild(node);
15363             return;
15364             
15365         }
15366         
15367         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
15368         
15369         // remove <a name=....> as rendering on yahoo mailer is borked with this.
15370         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
15371         
15372         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
15373         //    remove_keep_children = true;
15374         //}
15375         
15376         if (remove_keep_children) {
15377             this.cleanUpChildren(node);
15378             // inserts everything just before this node...
15379             while (node.childNodes.length) {
15380                 var cn = node.childNodes[0];
15381                 node.removeChild(cn);
15382                 node.parentNode.insertBefore(cn, node);
15383             }
15384             node.parentNode.removeChild(node);
15385             return;
15386         }
15387         
15388         if (!node.attributes || !node.attributes.length) {
15389             this.cleanUpChildren(node);
15390             return;
15391         }
15392         
15393         function cleanAttr(n,v)
15394         {
15395             
15396             if (v.match(/^\./) || v.match(/^\//)) {
15397                 return;
15398             }
15399             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
15400                 return;
15401             }
15402             if (v.match(/^#/)) {
15403                 return;
15404             }
15405 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
15406             node.removeAttribute(n);
15407             
15408         }
15409         
15410         function cleanStyle(n,v)
15411         {
15412             if (v.match(/expression/)) { //XSS?? should we even bother..
15413                 node.removeAttribute(n);
15414                 return;
15415             }
15416             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
15417             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
15418             
15419             
15420             var parts = v.split(/;/);
15421             var clean = [];
15422             
15423             Roo.each(parts, function(p) {
15424                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
15425                 if (!p.length) {
15426                     return true;
15427                 }
15428                 var l = p.split(':').shift().replace(/\s+/g,'');
15429                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
15430                 
15431                 if ( cblack.indexOf(l) > -1) {
15432 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
15433                     //node.removeAttribute(n);
15434                     return true;
15435                 }
15436                 //Roo.log()
15437                 // only allow 'c whitelisted system attributes'
15438                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
15439 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
15440                     //node.removeAttribute(n);
15441                     return true;
15442                 }
15443                 
15444                 
15445                  
15446                 
15447                 clean.push(p);
15448                 return true;
15449             });
15450             if (clean.length) { 
15451                 node.setAttribute(n, clean.join(';'));
15452             } else {
15453                 node.removeAttribute(n);
15454             }
15455             
15456         }
15457         
15458         
15459         for (var i = node.attributes.length-1; i > -1 ; i--) {
15460             var a = node.attributes[i];
15461             //console.log(a);
15462             
15463             if (a.name.toLowerCase().substr(0,2)=='on')  {
15464                 node.removeAttribute(a.name);
15465                 continue;
15466             }
15467             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
15468                 node.removeAttribute(a.name);
15469                 continue;
15470             }
15471             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
15472                 cleanAttr(a.name,a.value); // fixme..
15473                 continue;
15474             }
15475             if (a.name == 'style') {
15476                 cleanStyle(a.name,a.value);
15477                 continue;
15478             }
15479             /// clean up MS crap..
15480             // tecnically this should be a list of valid class'es..
15481             
15482             
15483             if (a.name == 'class') {
15484                 if (a.value.match(/^Mso/)) {
15485                     node.className = '';
15486                 }
15487                 
15488                 if (a.value.match(/body/)) {
15489                     node.className = '';
15490                 }
15491                 continue;
15492             }
15493             
15494             // style cleanup!?
15495             // class cleanup?
15496             
15497         }
15498         
15499         
15500         this.cleanUpChildren(node);
15501         
15502         
15503     },
15504     /**
15505      * Clean up MS wordisms...
15506      */
15507     cleanWord : function(node)
15508     {
15509         var _t = this;
15510         var cleanWordChildren = function()
15511         {
15512             if (!node.childNodes.length) {
15513                 return;
15514             }
15515             for (var i = node.childNodes.length-1; i > -1 ; i--) {
15516                _t.cleanWord(node.childNodes[i]);
15517             }
15518         }
15519         
15520         
15521         if (!node) {
15522             this.cleanWord(this.doc.body);
15523             return;
15524         }
15525         if (node.nodeName == "#text") {
15526             // clean up silly Windows -- stuff?
15527             return; 
15528         }
15529         if (node.nodeName == "#comment") {
15530             node.parentNode.removeChild(node);
15531             // clean up silly Windows -- stuff?
15532             return; 
15533         }
15534         
15535         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
15536             node.parentNode.removeChild(node);
15537             return;
15538         }
15539         
15540         // remove - but keep children..
15541         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
15542             while (node.childNodes.length) {
15543                 var cn = node.childNodes[0];
15544                 node.removeChild(cn);
15545                 node.parentNode.insertBefore(cn, node);
15546             }
15547             node.parentNode.removeChild(node);
15548             cleanWordChildren();
15549             return;
15550         }
15551         // clean styles
15552         if (node.className.length) {
15553             
15554             var cn = node.className.split(/\W+/);
15555             var cna = [];
15556             Roo.each(cn, function(cls) {
15557                 if (cls.match(/Mso[a-zA-Z]+/)) {
15558                     return;
15559                 }
15560                 cna.push(cls);
15561             });
15562             node.className = cna.length ? cna.join(' ') : '';
15563             if (!cna.length) {
15564                 node.removeAttribute("class");
15565             }
15566         }
15567         
15568         if (node.hasAttribute("lang")) {
15569             node.removeAttribute("lang");
15570         }
15571         
15572         if (node.hasAttribute("style")) {
15573             
15574             var styles = node.getAttribute("style").split(";");
15575             var nstyle = [];
15576             Roo.each(styles, function(s) {
15577                 if (!s.match(/:/)) {
15578                     return;
15579                 }
15580                 var kv = s.split(":");
15581                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
15582                     return;
15583                 }
15584                 // what ever is left... we allow.
15585                 nstyle.push(s);
15586             });
15587             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
15588             if (!nstyle.length) {
15589                 node.removeAttribute('style');
15590             }
15591         }
15592         
15593         cleanWordChildren();
15594         
15595         
15596     },
15597     domToHTML : function(currentElement, depth, nopadtext) {
15598         
15599             depth = depth || 0;
15600             nopadtext = nopadtext || false;
15601         
15602             if (!currentElement) {
15603                 return this.domToHTML(this.doc.body);
15604             }
15605             
15606             //Roo.log(currentElement);
15607             var j;
15608             var allText = false;
15609             var nodeName = currentElement.nodeName;
15610             var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
15611             
15612             if  (nodeName == '#text') {
15613                 return currentElement.nodeValue;
15614             }
15615             
15616             
15617             var ret = '';
15618             if (nodeName != 'BODY') {
15619                  
15620                 var i = 0;
15621                 // Prints the node tagName, such as <A>, <IMG>, etc
15622                 if (tagName) {
15623                     var attr = [];
15624                     for(i = 0; i < currentElement.attributes.length;i++) {
15625                         // quoting?
15626                         var aname = currentElement.attributes.item(i).name;
15627                         if (!currentElement.attributes.item(i).value.length) {
15628                             continue;
15629                         }
15630                         attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
15631                     }
15632                     
15633                     ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
15634                 } 
15635                 else {
15636                     
15637                     // eack
15638                 }
15639             } else {
15640                 tagName = false;
15641             }
15642             if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
15643                 return ret;
15644             }
15645             if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
15646                 nopadtext = true;
15647             }
15648             
15649             
15650             // Traverse the tree
15651             i = 0;
15652             var currentElementChild = currentElement.childNodes.item(i);
15653             var allText = true;
15654             var innerHTML  = '';
15655             lastnode = '';
15656             while (currentElementChild) {
15657                 // Formatting code (indent the tree so it looks nice on the screen)
15658                 var nopad = nopadtext;
15659                 if (lastnode == 'SPAN') {
15660                     nopad  = true;
15661                 }
15662                 // text
15663                 if  (currentElementChild.nodeName == '#text') {
15664                     var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
15665                     if (!nopad && toadd.length > 80) {
15666                         innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
15667                     }
15668                     innerHTML  += toadd;
15669                     
15670                     i++;
15671                     currentElementChild = currentElement.childNodes.item(i);
15672                     lastNode = '';
15673                     continue;
15674                 }
15675                 allText = false;
15676                 
15677                 innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
15678                     
15679                 // Recursively traverse the tree structure of the child node
15680                 innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
15681                 lastnode = currentElementChild.nodeName;
15682                 i++;
15683                 currentElementChild=currentElement.childNodes.item(i);
15684             }
15685             
15686             ret += innerHTML;
15687             
15688             if (!allText) {
15689                     // The remaining code is mostly for formatting the tree
15690                 ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
15691             }
15692             
15693             
15694             if (tagName) {
15695                 ret+= "</"+tagName+">";
15696             }
15697             return ret;
15698             
15699         }
15700     
15701     // hide stuff that is not compatible
15702     /**
15703      * @event blur
15704      * @hide
15705      */
15706     /**
15707      * @event change
15708      * @hide
15709      */
15710     /**
15711      * @event focus
15712      * @hide
15713      */
15714     /**
15715      * @event specialkey
15716      * @hide
15717      */
15718     /**
15719      * @cfg {String} fieldClass @hide
15720      */
15721     /**
15722      * @cfg {String} focusClass @hide
15723      */
15724     /**
15725      * @cfg {String} autoCreate @hide
15726      */
15727     /**
15728      * @cfg {String} inputType @hide
15729      */
15730     /**
15731      * @cfg {String} invalidClass @hide
15732      */
15733     /**
15734      * @cfg {String} invalidText @hide
15735      */
15736     /**
15737      * @cfg {String} msgFx @hide
15738      */
15739     /**
15740      * @cfg {String} validateOnBlur @hide
15741      */
15742 });
15743
15744 Roo.HtmlEditorCore.white = [
15745         'area', 'br', 'img', 'input', 'hr', 'wbr',
15746         
15747        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
15748        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
15749        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
15750        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
15751        'table',   'ul',         'xmp', 
15752        
15753        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
15754       'thead',   'tr', 
15755      
15756       'dir', 'menu', 'ol', 'ul', 'dl',
15757        
15758       'embed',  'object'
15759 ];
15760
15761
15762 Roo.HtmlEditorCore.black = [
15763     //    'embed',  'object', // enable - backend responsiblity to clean thiese
15764         'applet', // 
15765         'base',   'basefont', 'bgsound', 'blink',  'body', 
15766         'frame',  'frameset', 'head',    'html',   'ilayer', 
15767         'iframe', 'layer',  'link',     'meta',    'object',   
15768         'script', 'style' ,'title',  'xml' // clean later..
15769 ];
15770 Roo.HtmlEditorCore.clean = [
15771     'script', 'style', 'title', 'xml'
15772 ];
15773 Roo.HtmlEditorCore.remove = [
15774     'font'
15775 ];
15776 // attributes..
15777
15778 Roo.HtmlEditorCore.ablack = [
15779     'on'
15780 ];
15781     
15782 Roo.HtmlEditorCore.aclean = [ 
15783     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
15784 ];
15785
15786 // protocols..
15787 Roo.HtmlEditorCore.pwhite= [
15788         'http',  'https',  'mailto'
15789 ];
15790
15791 // white listed style attributes.
15792 Roo.HtmlEditorCore.cwhite= [
15793       //  'text-align', /// default is to allow most things..
15794       
15795          
15796 //        'font-size'//??
15797 ];
15798
15799 // black listed style attributes.
15800 Roo.HtmlEditorCore.cblack= [
15801       //  'font-size' -- this can be set by the project 
15802 ];
15803
15804
15805 Roo.HtmlEditorCore.swapCodes   =[ 
15806     [    8211, "--" ], 
15807     [    8212, "--" ], 
15808     [    8216,  "'" ],  
15809     [    8217, "'" ],  
15810     [    8220, '"' ],  
15811     [    8221, '"' ],  
15812     [    8226, "*" ],  
15813     [    8230, "..." ]
15814 ]; 
15815
15816     /*
15817  * - LGPL
15818  *
15819  * HtmlEditor
15820  * 
15821  */
15822
15823 /**
15824  * @class Roo.bootstrap.HtmlEditor
15825  * @extends Roo.bootstrap.TextArea
15826  * Bootstrap HtmlEditor class
15827
15828  * @constructor
15829  * Create a new HtmlEditor
15830  * @param {Object} config The config object
15831  */
15832
15833 Roo.bootstrap.HtmlEditor = function(config){
15834     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
15835     if (!this.toolbars) {
15836         this.toolbars = [];
15837     }
15838     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
15839     this.addEvents({
15840             /**
15841              * @event initialize
15842              * Fires when the editor is fully initialized (including the iframe)
15843              * @param {HtmlEditor} this
15844              */
15845             initialize: true,
15846             /**
15847              * @event activate
15848              * Fires when the editor is first receives the focus. Any insertion must wait
15849              * until after this event.
15850              * @param {HtmlEditor} this
15851              */
15852             activate: true,
15853              /**
15854              * @event beforesync
15855              * Fires before the textarea is updated with content from the editor iframe. Return false
15856              * to cancel the sync.
15857              * @param {HtmlEditor} this
15858              * @param {String} html
15859              */
15860             beforesync: true,
15861              /**
15862              * @event beforepush
15863              * Fires before the iframe editor is updated with content from the textarea. Return false
15864              * to cancel the push.
15865              * @param {HtmlEditor} this
15866              * @param {String} html
15867              */
15868             beforepush: true,
15869              /**
15870              * @event sync
15871              * Fires when the textarea is updated with content from the editor iframe.
15872              * @param {HtmlEditor} this
15873              * @param {String} html
15874              */
15875             sync: true,
15876              /**
15877              * @event push
15878              * Fires when the iframe editor is updated with content from the textarea.
15879              * @param {HtmlEditor} this
15880              * @param {String} html
15881              */
15882             push: true,
15883              /**
15884              * @event editmodechange
15885              * Fires when the editor switches edit modes
15886              * @param {HtmlEditor} this
15887              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
15888              */
15889             editmodechange: true,
15890             /**
15891              * @event editorevent
15892              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
15893              * @param {HtmlEditor} this
15894              */
15895             editorevent: true,
15896             /**
15897              * @event firstfocus
15898              * Fires when on first focus - needed by toolbars..
15899              * @param {HtmlEditor} this
15900              */
15901             firstfocus: true,
15902             /**
15903              * @event autosave
15904              * Auto save the htmlEditor value as a file into Events
15905              * @param {HtmlEditor} this
15906              */
15907             autosave: true,
15908             /**
15909              * @event savedpreview
15910              * preview the saved version of htmlEditor
15911              * @param {HtmlEditor} this
15912              */
15913             savedpreview: true
15914         });
15915 };
15916
15917
15918 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
15919     
15920     
15921       /**
15922      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
15923      */
15924     toolbars : false,
15925    
15926      /**
15927      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
15928      *                        Roo.resizable.
15929      */
15930     resizable : false,
15931      /**
15932      * @cfg {Number} height (in pixels)
15933      */   
15934     height: 300,
15935    /**
15936      * @cfg {Number} width (in pixels)
15937      */   
15938     width: false,
15939     
15940     /**
15941      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
15942      * 
15943      */
15944     stylesheets: false,
15945     
15946     // id of frame..
15947     frameId: false,
15948     
15949     // private properties
15950     validationEvent : false,
15951     deferHeight: true,
15952     initialized : false,
15953     activated : false,
15954     
15955     onFocus : Roo.emptyFn,
15956     iframePad:3,
15957     hideMode:'offsets',
15958     
15959     
15960     tbContainer : false,
15961     
15962     toolbarContainer :function() {
15963         return this.wrap.select('.x-html-editor-tb',true).first();
15964     },
15965
15966     /**
15967      * Protected method that will not generally be called directly. It
15968      * is called when the editor creates its toolbar. Override this method if you need to
15969      * add custom toolbar buttons.
15970      * @param {HtmlEditor} editor
15971      */
15972     createToolbar : function(){
15973         
15974         Roo.log("create toolbars");
15975         
15976         this.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard({editor: this} ) ];
15977         this.toolbars[0].render(this.toolbarContainer());
15978         
15979         return;
15980         
15981 //        if (!editor.toolbars || !editor.toolbars.length) {
15982 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
15983 //        }
15984 //        
15985 //        for (var i =0 ; i < editor.toolbars.length;i++) {
15986 //            editor.toolbars[i] = Roo.factory(
15987 //                    typeof(editor.toolbars[i]) == 'string' ?
15988 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
15989 //                Roo.bootstrap.HtmlEditor);
15990 //            editor.toolbars[i].init(editor);
15991 //        }
15992     },
15993
15994      
15995     // private
15996     onRender : function(ct, position)
15997     {
15998        // Roo.log("Call onRender: " + this.xtype);
15999         var _t = this;
16000         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
16001       
16002         this.wrap = this.inputEl().wrap({
16003             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
16004         });
16005         
16006         this.editorcore.onRender(ct, position);
16007          
16008         if (this.resizable) {
16009             this.resizeEl = new Roo.Resizable(this.wrap, {
16010                 pinned : true,
16011                 wrap: true,
16012                 dynamic : true,
16013                 minHeight : this.height,
16014                 height: this.height,
16015                 handles : this.resizable,
16016                 width: this.width,
16017                 listeners : {
16018                     resize : function(r, w, h) {
16019                         _t.onResize(w,h); // -something
16020                     }
16021                 }
16022             });
16023             
16024         }
16025         this.createToolbar(this);
16026        
16027         
16028         if(!this.width && this.resizable){
16029             this.setSize(this.wrap.getSize());
16030         }
16031         if (this.resizeEl) {
16032             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
16033             // should trigger onReize..
16034         }
16035         
16036     },
16037
16038     // private
16039     onResize : function(w, h)
16040     {
16041         Roo.log('resize: ' +w + ',' + h );
16042         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
16043         var ew = false;
16044         var eh = false;
16045         
16046         if(this.inputEl() ){
16047             if(typeof w == 'number'){
16048                 var aw = w - this.wrap.getFrameWidth('lr');
16049                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
16050                 ew = aw;
16051             }
16052             if(typeof h == 'number'){
16053                  var tbh = -11;  // fixme it needs to tool bar size!
16054                 for (var i =0; i < this.toolbars.length;i++) {
16055                     // fixme - ask toolbars for heights?
16056                     tbh += this.toolbars[i].el.getHeight();
16057                     //if (this.toolbars[i].footer) {
16058                     //    tbh += this.toolbars[i].footer.el.getHeight();
16059                     //}
16060                 }
16061               
16062                 
16063                 
16064                 
16065                 
16066                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
16067                 ah -= 5; // knock a few pixes off for look..
16068                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
16069                 var eh = ah;
16070             }
16071         }
16072         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
16073         this.editorcore.onResize(ew,eh);
16074         
16075     },
16076
16077     /**
16078      * Toggles the editor between standard and source edit mode.
16079      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
16080      */
16081     toggleSourceEdit : function(sourceEditMode)
16082     {
16083         this.editorcore.toggleSourceEdit(sourceEditMode);
16084         
16085         if(this.editorcore.sourceEditMode){
16086             Roo.log('editor - showing textarea');
16087             
16088 //            Roo.log('in');
16089 //            Roo.log(this.syncValue());
16090             this.syncValue();
16091             this.inputEl().removeClass('hide');
16092             this.inputEl().dom.removeAttribute('tabIndex');
16093             this.inputEl().focus();
16094         }else{
16095             Roo.log('editor - hiding textarea');
16096 //            Roo.log('out')
16097 //            Roo.log(this.pushValue()); 
16098             this.pushValue();
16099             
16100             this.inputEl().addClass('hide');
16101             this.inputEl().dom.setAttribute('tabIndex', -1);
16102             //this.deferFocus();
16103         }
16104          
16105         if(this.resizable){
16106             this.setSize(this.wrap.getSize());
16107         }
16108         
16109         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
16110     },
16111  
16112     // private (for BoxComponent)
16113     adjustSize : Roo.BoxComponent.prototype.adjustSize,
16114
16115     // private (for BoxComponent)
16116     getResizeEl : function(){
16117         return this.wrap;
16118     },
16119
16120     // private (for BoxComponent)
16121     getPositionEl : function(){
16122         return this.wrap;
16123     },
16124
16125     // private
16126     initEvents : function(){
16127         this.originalValue = this.getValue();
16128     },
16129
16130 //    /**
16131 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
16132 //     * @method
16133 //     */
16134 //    markInvalid : Roo.emptyFn,
16135 //    /**
16136 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
16137 //     * @method
16138 //     */
16139 //    clearInvalid : Roo.emptyFn,
16140
16141     setValue : function(v){
16142         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
16143         this.editorcore.pushValue();
16144     },
16145
16146      
16147     // private
16148     deferFocus : function(){
16149         this.focus.defer(10, this);
16150     },
16151
16152     // doc'ed in Field
16153     focus : function(){
16154         this.editorcore.focus();
16155         
16156     },
16157       
16158
16159     // private
16160     onDestroy : function(){
16161         
16162         
16163         
16164         if(this.rendered){
16165             
16166             for (var i =0; i < this.toolbars.length;i++) {
16167                 // fixme - ask toolbars for heights?
16168                 this.toolbars[i].onDestroy();
16169             }
16170             
16171             this.wrap.dom.innerHTML = '';
16172             this.wrap.remove();
16173         }
16174     },
16175
16176     // private
16177     onFirstFocus : function(){
16178         //Roo.log("onFirstFocus");
16179         this.editorcore.onFirstFocus();
16180          for (var i =0; i < this.toolbars.length;i++) {
16181             this.toolbars[i].onFirstFocus();
16182         }
16183         
16184     },
16185     
16186     // private
16187     syncValue : function()
16188     {   
16189         this.editorcore.syncValue();
16190     },
16191     
16192     pushValue : function()
16193     {   
16194         this.editorcore.pushValue();
16195     }
16196      
16197     
16198     // hide stuff that is not compatible
16199     /**
16200      * @event blur
16201      * @hide
16202      */
16203     /**
16204      * @event change
16205      * @hide
16206      */
16207     /**
16208      * @event focus
16209      * @hide
16210      */
16211     /**
16212      * @event specialkey
16213      * @hide
16214      */
16215     /**
16216      * @cfg {String} fieldClass @hide
16217      */
16218     /**
16219      * @cfg {String} focusClass @hide
16220      */
16221     /**
16222      * @cfg {String} autoCreate @hide
16223      */
16224     /**
16225      * @cfg {String} inputType @hide
16226      */
16227     /**
16228      * @cfg {String} invalidClass @hide
16229      */
16230     /**
16231      * @cfg {String} invalidText @hide
16232      */
16233     /**
16234      * @cfg {String} msgFx @hide
16235      */
16236     /**
16237      * @cfg {String} validateOnBlur @hide
16238      */
16239 });
16240  
16241     
16242    
16243    
16244    
16245       
16246
16247 /**
16248  * @class Roo.bootstrap.HtmlEditorToolbar1
16249  * Basic Toolbar
16250  * 
16251  * Usage:
16252  *
16253  new Roo.bootstrap.HtmlEditor({
16254     ....
16255     toolbars : [
16256         new Roo.bootstrap.HtmlEditorToolbar1({
16257             disable : { fonts: 1 , format: 1, ..., ... , ...],
16258             btns : [ .... ]
16259         })
16260     }
16261      
16262  * 
16263  * @cfg {Object} disable List of elements to disable..
16264  * @cfg {Array} btns List of additional buttons.
16265  * 
16266  * 
16267  * NEEDS Extra CSS? 
16268  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
16269  */
16270  
16271 Roo.bootstrap.HtmlEditor.ToolbarStandard = function(config)
16272 {
16273     
16274     Roo.apply(this, config);
16275     
16276     // default disabled, based on 'good practice'..
16277     this.disable = this.disable || {};
16278     Roo.applyIf(this.disable, {
16279         fontSize : true,
16280         colors : true,
16281         specialElements : true
16282     });
16283     Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.constructor.call(this, config);
16284     
16285     this.editor = config.editor;
16286     this.editorcore = config.editor.editorcore;
16287     
16288     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
16289     
16290     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
16291     // dont call parent... till later.
16292 }
16293 Roo.extend(Roo.bootstrap.HtmlEditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
16294     
16295     
16296     bar : true,
16297     
16298     editor : false,
16299     editorcore : false,
16300     
16301     
16302     formats : [
16303         "p" ,  
16304         "h1","h2","h3","h4","h5","h6", 
16305         "pre", "code", 
16306         "abbr", "acronym", "address", "cite", "samp", "var",
16307         'div','span'
16308     ],
16309     
16310     onRender : function(ct, position)
16311     {
16312        // Roo.log("Call onRender: " + this.xtype);
16313         
16314        Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
16315        Roo.log(this.el);
16316        this.el.dom.style.marginBottom = '0';
16317        var _this = this;
16318        var editorcore = this.editorcore;
16319        var editor= this.editor;
16320        
16321        var children = [];
16322        var btn = function(id,cmd , toggle, handler){
16323        
16324             var  event = toggle ? 'toggle' : 'click';
16325        
16326             var a = {
16327                 size : 'sm',
16328                 xtype: 'Button',
16329                 xns: Roo.bootstrap,
16330                 glyphicon : id,
16331                 cmd : id || cmd,
16332                 enableToggle:toggle !== false,
16333                 //html : 'submit'
16334                 pressed : toggle ? false : null,
16335                 listeners : {}
16336             }
16337             a.listeners[toggle ? 'toggle' : 'click'] = function() {
16338                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
16339             }
16340             children.push(a);
16341             return a;
16342        }
16343         
16344         var style = {
16345                 xtype: 'Button',
16346                 size : 'sm',
16347                 xns: Roo.bootstrap,
16348                 glyphicon : 'font',
16349                 //html : 'submit'
16350                 menu : {
16351                     xtype: 'Menu',
16352                     xns: Roo.bootstrap,
16353                     items:  []
16354                 }
16355         };
16356         Roo.each(this.formats, function(f) {
16357             style.menu.items.push({
16358                 xtype :'MenuItem',
16359                 xns: Roo.bootstrap,
16360                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
16361                 tagname : f,
16362                 listeners : {
16363                     click : function()
16364                     {
16365                         editorcore.insertTag(this.tagname);
16366                         editor.focus();
16367                     }
16368                 }
16369                 
16370             });
16371         });
16372          children.push(style);   
16373             
16374             
16375         btn('bold',false,true);
16376         btn('italic',false,true);
16377         btn('align-left', 'justifyleft',true);
16378         btn('align-center', 'justifycenter',true);
16379         btn('align-right' , 'justifyright',true);
16380         btn('link', false, false, function(btn) {
16381             //Roo.log("create link?");
16382             var url = prompt(this.createLinkText, this.defaultLinkValue);
16383             if(url && url != 'http:/'+'/'){
16384                 this.editorcore.relayCmd('createlink', url);
16385             }
16386         }),
16387         btn('list','insertunorderedlist',true);
16388         btn('pencil', false,true, function(btn){
16389                 Roo.log(this);
16390                 
16391                 this.toggleSourceEdit(btn.pressed);
16392         });
16393         /*
16394         var cog = {
16395                 xtype: 'Button',
16396                 size : 'sm',
16397                 xns: Roo.bootstrap,
16398                 glyphicon : 'cog',
16399                 //html : 'submit'
16400                 menu : {
16401                     xtype: 'Menu',
16402                     xns: Roo.bootstrap,
16403                     items:  []
16404                 }
16405         };
16406         
16407         cog.menu.items.push({
16408             xtype :'MenuItem',
16409             xns: Roo.bootstrap,
16410             html : Clean styles,
16411             tagname : f,
16412             listeners : {
16413                 click : function()
16414                 {
16415                     editorcore.insertTag(this.tagname);
16416                     editor.focus();
16417                 }
16418             }
16419             
16420         });
16421        */
16422         
16423          
16424        this.xtype = 'NavSimplebar';
16425         
16426         for(var i=0;i< children.length;i++) {
16427             
16428             this.buttons.add(this.addxtypeChild(children[i]));
16429             
16430         }
16431         
16432         editor.on('editorevent', this.updateToolbar, this);
16433     },
16434     onBtnClick : function(id)
16435     {
16436        this.editorcore.relayCmd(id);
16437        this.editorcore.focus();
16438     },
16439     
16440     /**
16441      * Protected method that will not generally be called directly. It triggers
16442      * a toolbar update by reading the markup state of the current selection in the editor.
16443      */
16444     updateToolbar: function(){
16445
16446         if(!this.editorcore.activated){
16447             this.editor.onFirstFocus(); // is this neeed?
16448             return;
16449         }
16450
16451         var btns = this.buttons; 
16452         var doc = this.editorcore.doc;
16453         btns.get('bold').setActive(doc.queryCommandState('bold'));
16454         btns.get('italic').setActive(doc.queryCommandState('italic'));
16455         //btns.get('underline').setActive(doc.queryCommandState('underline'));
16456         
16457         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
16458         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
16459         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
16460         
16461         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
16462         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
16463          /*
16464         
16465         var ans = this.editorcore.getAllAncestors();
16466         if (this.formatCombo) {
16467             
16468             
16469             var store = this.formatCombo.store;
16470             this.formatCombo.setValue("");
16471             for (var i =0; i < ans.length;i++) {
16472                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
16473                     // select it..
16474                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
16475                     break;
16476                 }
16477             }
16478         }
16479         
16480         
16481         
16482         // hides menus... - so this cant be on a menu...
16483         Roo.bootstrap.MenuMgr.hideAll();
16484         */
16485         Roo.bootstrap.MenuMgr.hideAll();
16486         //this.editorsyncValue();
16487     },
16488     onFirstFocus: function() {
16489         this.buttons.each(function(item){
16490            item.enable();
16491         });
16492     },
16493     toggleSourceEdit : function(sourceEditMode){
16494         
16495           
16496         if(sourceEditMode){
16497             Roo.log("disabling buttons");
16498            this.buttons.each( function(item){
16499                 if(item.cmd != 'pencil'){
16500                     item.disable();
16501                 }
16502             });
16503           
16504         }else{
16505             Roo.log("enabling buttons");
16506             if(this.editorcore.initialized){
16507                 this.buttons.each( function(item){
16508                     item.enable();
16509                 });
16510             }
16511             
16512         }
16513         Roo.log("calling toggole on editor");
16514         // tell the editor that it's been pressed..
16515         this.editor.toggleSourceEdit(sourceEditMode);
16516        
16517     }
16518 });
16519
16520
16521
16522
16523
16524 /**
16525  * @class Roo.bootstrap.Table.AbstractSelectionModel
16526  * @extends Roo.util.Observable
16527  * Abstract base class for grid SelectionModels.  It provides the interface that should be
16528  * implemented by descendant classes.  This class should not be directly instantiated.
16529  * @constructor
16530  */
16531 Roo.bootstrap.Table.AbstractSelectionModel = function(){
16532     this.locked = false;
16533     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
16534 };
16535
16536
16537 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
16538     /** @ignore Called by the grid automatically. Do not call directly. */
16539     init : function(grid){
16540         this.grid = grid;
16541         this.initEvents();
16542     },
16543
16544     /**
16545      * Locks the selections.
16546      */
16547     lock : function(){
16548         this.locked = true;
16549     },
16550
16551     /**
16552      * Unlocks the selections.
16553      */
16554     unlock : function(){
16555         this.locked = false;
16556     },
16557
16558     /**
16559      * Returns true if the selections are locked.
16560      * @return {Boolean}
16561      */
16562     isLocked : function(){
16563         return this.locked;
16564     }
16565 });
16566 /**
16567  * @class Roo.bootstrap.Table.ColumnModel
16568  * @extends Roo.util.Observable
16569  * This is the default implementation of a ColumnModel used by the bootstrap table. It defines
16570  * the columns in the table.
16571  
16572  * @constructor
16573  * @param {Object} config An Array of column config objects. See this class's
16574  * config objects for details.
16575 */
16576 Roo.bootstrap.Table.ColumnModel = function(config){
16577         /**
16578      * The config passed into the constructor
16579      */
16580     this.config = config;
16581     this.lookup = {};
16582
16583     // if no id, create one
16584     // if the column does not have a dataIndex mapping,
16585     // map it to the order it is in the config
16586     for(var i = 0, len = config.length; i < len; i++){
16587         var c = config[i];
16588         if(typeof c.dataIndex == "undefined"){
16589             c.dataIndex = i;
16590         }
16591         if(typeof c.renderer == "string"){
16592             c.renderer = Roo.util.Format[c.renderer];
16593         }
16594         if(typeof c.id == "undefined"){
16595             c.id = Roo.id();
16596         }
16597 //        if(c.editor && c.editor.xtype){
16598 //            c.editor  = Roo.factory(c.editor, Roo.grid);
16599 //        }
16600 //        if(c.editor && c.editor.isFormField){
16601 //            c.editor = new Roo.grid.GridEditor(c.editor);
16602 //        }
16603
16604         this.lookup[c.id] = c;
16605     }
16606
16607     /**
16608      * The width of columns which have no width specified (defaults to 100)
16609      * @type Number
16610      */
16611     this.defaultWidth = 100;
16612
16613     /**
16614      * Default sortable of columns which have no sortable specified (defaults to false)
16615      * @type Boolean
16616      */
16617     this.defaultSortable = false;
16618
16619     this.addEvents({
16620         /**
16621              * @event widthchange
16622              * Fires when the width of a column changes.
16623              * @param {ColumnModel} this
16624              * @param {Number} columnIndex The column index
16625              * @param {Number} newWidth The new width
16626              */
16627             "widthchange": true,
16628         /**
16629              * @event headerchange
16630              * Fires when the text of a header changes.
16631              * @param {ColumnModel} this
16632              * @param {Number} columnIndex The column index
16633              * @param {Number} newText The new header text
16634              */
16635             "headerchange": true,
16636         /**
16637              * @event hiddenchange
16638              * Fires when a column is hidden or "unhidden".
16639              * @param {ColumnModel} this
16640              * @param {Number} columnIndex The column index
16641              * @param {Boolean} hidden true if hidden, false otherwise
16642              */
16643             "hiddenchange": true,
16644             /**
16645          * @event columnmoved
16646          * Fires when a column is moved.
16647          * @param {ColumnModel} this
16648          * @param {Number} oldIndex
16649          * @param {Number} newIndex
16650          */
16651         "columnmoved" : true,
16652         /**
16653          * @event columlockchange
16654          * Fires when a column's locked state is changed
16655          * @param {ColumnModel} this
16656          * @param {Number} colIndex
16657          * @param {Boolean} locked true if locked
16658          */
16659         "columnlockchange" : true
16660     });
16661     Roo.bootstrap.Table.ColumnModel.superclass.constructor.call(this);
16662 };
16663 Roo.extend(Roo.bootstrap.Table.ColumnModel, Roo.util.Observable, {
16664     /**
16665      * @cfg {String} header The header text to display in the Grid view.
16666      */
16667     /**
16668      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
16669      * {@link Roo.data.Record} definition from which to draw the column's value. If not
16670      * specified, the column's index is used as an index into the Record's data Array.
16671      */
16672     /**
16673      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
16674      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
16675      */
16676     /**
16677      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
16678      * Defaults to the value of the {@link #defaultSortable} property.
16679      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
16680      */
16681     /**
16682      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
16683      */
16684     /**
16685      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
16686      */
16687     /**
16688      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
16689      */
16690     /**
16691      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
16692      */
16693     /**
16694      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
16695      * given the cell's data value. See {@link #setRenderer}. If not specified, the
16696      * default renderer uses the raw data value.
16697      */
16698     /**
16699      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
16700      */
16701
16702     /**
16703      * Returns the id of the column at the specified index.
16704      * @param {Number} index The column index
16705      * @return {String} the id
16706      */
16707     getColumnId : function(index){
16708         return this.config[index].id;
16709     },
16710
16711     /**
16712      * Returns the column for a specified id.
16713      * @param {String} id The column id
16714      * @return {Object} the column
16715      */
16716     getColumnById : function(id){
16717         return this.lookup[id];
16718     },
16719
16720     
16721     /**
16722      * Returns the column for a specified dataIndex.
16723      * @param {String} dataIndex The column dataIndex
16724      * @return {Object|Boolean} the column or false if not found
16725      */
16726     getColumnByDataIndex: function(dataIndex){
16727         var index = this.findColumnIndex(dataIndex);
16728         return index > -1 ? this.config[index] : false;
16729     },
16730     
16731     /**
16732      * Returns the index for a specified column id.
16733      * @param {String} id The column id
16734      * @return {Number} the index, or -1 if not found
16735      */
16736     getIndexById : function(id){
16737         for(var i = 0, len = this.config.length; i < len; i++){
16738             if(this.config[i].id == id){
16739                 return i;
16740             }
16741         }
16742         return -1;
16743     },
16744     
16745     /**
16746      * Returns the index for a specified column dataIndex.
16747      * @param {String} dataIndex The column dataIndex
16748      * @return {Number} the index, or -1 if not found
16749      */
16750     
16751     findColumnIndex : function(dataIndex){
16752         for(var i = 0, len = this.config.length; i < len; i++){
16753             if(this.config[i].dataIndex == dataIndex){
16754                 return i;
16755             }
16756         }
16757         return -1;
16758     },
16759     
16760     
16761     moveColumn : function(oldIndex, newIndex){
16762         var c = this.config[oldIndex];
16763         this.config.splice(oldIndex, 1);
16764         this.config.splice(newIndex, 0, c);
16765         this.dataMap = null;
16766         this.fireEvent("columnmoved", this, oldIndex, newIndex);
16767     },
16768
16769     isLocked : function(colIndex){
16770         return this.config[colIndex].locked === true;
16771     },
16772
16773     setLocked : function(colIndex, value, suppressEvent){
16774         if(this.isLocked(colIndex) == value){
16775             return;
16776         }
16777         this.config[colIndex].locked = value;
16778         if(!suppressEvent){
16779             this.fireEvent("columnlockchange", this, colIndex, value);
16780         }
16781     },
16782
16783     getTotalLockedWidth : function(){
16784         var totalWidth = 0;
16785         for(var i = 0; i < this.config.length; i++){
16786             if(this.isLocked(i) && !this.isHidden(i)){
16787                 this.totalWidth += this.getColumnWidth(i);
16788             }
16789         }
16790         return totalWidth;
16791     },
16792
16793     getLockedCount : function(){
16794         for(var i = 0, len = this.config.length; i < len; i++){
16795             if(!this.isLocked(i)){
16796                 return i;
16797             }
16798         }
16799     },
16800
16801     /**
16802      * Returns the number of columns.
16803      * @return {Number}
16804      */
16805     getColumnCount : function(visibleOnly){
16806         if(visibleOnly === true){
16807             var c = 0;
16808             for(var i = 0, len = this.config.length; i < len; i++){
16809                 if(!this.isHidden(i)){
16810                     c++;
16811                 }
16812             }
16813             return c;
16814         }
16815         return this.config.length;
16816     },
16817
16818     /**
16819      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
16820      * @param {Function} fn
16821      * @param {Object} scope (optional)
16822      * @return {Array} result
16823      */
16824     getColumnsBy : function(fn, scope){
16825         var r = [];
16826         for(var i = 0, len = this.config.length; i < len; i++){
16827             var c = this.config[i];
16828             if(fn.call(scope||this, c, i) === true){
16829                 r[r.length] = c;
16830             }
16831         }
16832         return r;
16833     },
16834
16835     /**
16836      * Returns true if the specified column is sortable.
16837      * @param {Number} col The column index
16838      * @return {Boolean}
16839      */
16840     isSortable : function(col){
16841         if(typeof this.config[col].sortable == "undefined"){
16842             return this.defaultSortable;
16843         }
16844         return this.config[col].sortable;
16845     },
16846
16847     /**
16848      * Returns the rendering (formatting) function defined for the column.
16849      * @param {Number} col The column index.
16850      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
16851      */
16852     getRenderer : function(col){
16853         if(!this.config[col].renderer){
16854             return Roo.bootstrap.Table.ColumnModel.defaultRenderer;
16855         }
16856         return this.config[col].renderer;
16857     },
16858
16859     /**
16860      * Sets the rendering (formatting) function for a column.
16861      * @param {Number} col The column index
16862      * @param {Function} fn The function to use to process the cell's raw data
16863      * to return HTML markup for the grid view. The render function is called with
16864      * the following parameters:<ul>
16865      * <li>Data value.</li>
16866      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
16867      * <li>css A CSS style string to apply to the table cell.</li>
16868      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
16869      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
16870      * <li>Row index</li>
16871      * <li>Column index</li>
16872      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
16873      */
16874     setRenderer : function(col, fn){
16875         this.config[col].renderer = fn;
16876     },
16877
16878     /**
16879      * Returns the width for the specified column.
16880      * @param {Number} col The column index
16881      * @return {Number}
16882      */
16883     getColumnWidth : function(col){
16884         return this.config[col].width * 1 || this.defaultWidth;
16885     },
16886
16887     /**
16888      * Sets the width for a column.
16889      * @param {Number} col The column index
16890      * @param {Number} width The new width
16891      */
16892     setColumnWidth : function(col, width, suppressEvent){
16893         this.config[col].width = width;
16894         this.totalWidth = null;
16895         if(!suppressEvent){
16896              this.fireEvent("widthchange", this, col, width);
16897         }
16898     },
16899
16900     /**
16901      * Returns the total width of all columns.
16902      * @param {Boolean} includeHidden True to include hidden column widths
16903      * @return {Number}
16904      */
16905     getTotalWidth : function(includeHidden){
16906         if(!this.totalWidth){
16907             this.totalWidth = 0;
16908             for(var i = 0, len = this.config.length; i < len; i++){
16909                 if(includeHidden || !this.isHidden(i)){
16910                     this.totalWidth += this.getColumnWidth(i);
16911                 }
16912             }
16913         }
16914         return this.totalWidth;
16915     },
16916
16917     /**
16918      * Returns the header for the specified column.
16919      * @param {Number} col The column index
16920      * @return {String}
16921      */
16922     getColumnHeader : function(col){
16923         return this.config[col].header;
16924     },
16925
16926     /**
16927      * Sets the header for a column.
16928      * @param {Number} col The column index
16929      * @param {String} header The new header
16930      */
16931     setColumnHeader : function(col, header){
16932         this.config[col].header = header;
16933         this.fireEvent("headerchange", this, col, header);
16934     },
16935
16936     /**
16937      * Returns the tooltip for the specified column.
16938      * @param {Number} col The column index
16939      * @return {String}
16940      */
16941     getColumnTooltip : function(col){
16942             return this.config[col].tooltip;
16943     },
16944     /**
16945      * Sets the tooltip for a column.
16946      * @param {Number} col The column index
16947      * @param {String} tooltip The new tooltip
16948      */
16949     setColumnTooltip : function(col, tooltip){
16950             this.config[col].tooltip = tooltip;
16951     },
16952
16953     /**
16954      * Returns the dataIndex for the specified column.
16955      * @param {Number} col The column index
16956      * @return {Number}
16957      */
16958     getDataIndex : function(col){
16959         return this.config[col].dataIndex;
16960     },
16961
16962     /**
16963      * Sets the dataIndex for a column.
16964      * @param {Number} col The column index
16965      * @param {Number} dataIndex The new dataIndex
16966      */
16967     setDataIndex : function(col, dataIndex){
16968         this.config[col].dataIndex = dataIndex;
16969     },
16970
16971     
16972     
16973     /**
16974      * Returns true if the cell is editable.
16975      * @param {Number} colIndex The column index
16976      * @param {Number} rowIndex The row index
16977      * @return {Boolean}
16978      */
16979     isCellEditable : function(colIndex, rowIndex){
16980         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
16981     },
16982
16983     /**
16984      * Returns the editor defined for the cell/column.
16985      * return false or null to disable editing.
16986      * @param {Number} colIndex The column index
16987      * @param {Number} rowIndex The row index
16988      * @return {Object}
16989      */
16990     getCellEditor : function(colIndex, rowIndex){
16991         return this.config[colIndex].editor;
16992     },
16993
16994     /**
16995      * Sets if a column is editable.
16996      * @param {Number} col The column index
16997      * @param {Boolean} editable True if the column is editable
16998      */
16999     setEditable : function(col, editable){
17000         this.config[col].editable = editable;
17001     },
17002
17003
17004     /**
17005      * Returns true if the column is hidden.
17006      * @param {Number} colIndex The column index
17007      * @return {Boolean}
17008      */
17009     isHidden : function(colIndex){
17010         return this.config[colIndex].hidden;
17011     },
17012
17013
17014     /**
17015      * Returns true if the column width cannot be changed
17016      */
17017     isFixed : function(colIndex){
17018         return this.config[colIndex].fixed;
17019     },
17020
17021     /**
17022      * Returns true if the column can be resized
17023      * @return {Boolean}
17024      */
17025     isResizable : function(colIndex){
17026         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
17027     },
17028     /**
17029      * Sets if a column is hidden.
17030      * @param {Number} colIndex The column index
17031      * @param {Boolean} hidden True if the column is hidden
17032      */
17033     setHidden : function(colIndex, hidden){
17034         this.config[colIndex].hidden = hidden;
17035         this.totalWidth = null;
17036         this.fireEvent("hiddenchange", this, colIndex, hidden);
17037     },
17038
17039     /**
17040      * Sets the editor for a column.
17041      * @param {Number} col The column index
17042      * @param {Object} editor The editor object
17043      */
17044     setEditor : function(col, editor){
17045         this.config[col].editor = editor;
17046     }
17047 });
17048
17049 Roo.bootstrap.Table.ColumnModel.defaultRenderer = function(value){
17050         if(typeof value == "string" && value.length < 1){
17051             return "&#160;";
17052         }
17053         return value;
17054 };
17055
17056 // Alias for backwards compatibility
17057 Roo.bootstrap.Table.DefaultColumnModel = Roo.bootstrap.Table.ColumnModel;
17058
17059 /**
17060  * @extends Roo.bootstrap.Table.AbstractSelectionModel
17061  * @class Roo.bootstrap.Table.RowSelectionModel
17062  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
17063  * It supports multiple selections and keyboard selection/navigation. 
17064  * @constructor
17065  * @param {Object} config
17066  */
17067
17068 Roo.bootstrap.Table.RowSelectionModel = function(config){
17069     Roo.apply(this, config);
17070     this.selections = new Roo.util.MixedCollection(false, function(o){
17071         return o.id;
17072     });
17073
17074     this.last = false;
17075     this.lastActive = false;
17076
17077     this.addEvents({
17078         /**
17079              * @event selectionchange
17080              * Fires when the selection changes
17081              * @param {SelectionModel} this
17082              */
17083             "selectionchange" : true,
17084         /**
17085              * @event afterselectionchange
17086              * Fires after the selection changes (eg. by key press or clicking)
17087              * @param {SelectionModel} this
17088              */
17089             "afterselectionchange" : true,
17090         /**
17091              * @event beforerowselect
17092              * Fires when a row is selected being selected, return false to cancel.
17093              * @param {SelectionModel} this
17094              * @param {Number} rowIndex The selected index
17095              * @param {Boolean} keepExisting False if other selections will be cleared
17096              */
17097             "beforerowselect" : true,
17098         /**
17099              * @event rowselect
17100              * Fires when a row is selected.
17101              * @param {SelectionModel} this
17102              * @param {Number} rowIndex The selected index
17103              * @param {Roo.data.Record} r The record
17104              */
17105             "rowselect" : true,
17106         /**
17107              * @event rowdeselect
17108              * Fires when a row is deselected.
17109              * @param {SelectionModel} this
17110              * @param {Number} rowIndex The selected index
17111              */
17112         "rowdeselect" : true
17113     });
17114     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
17115     this.locked = false;
17116 };
17117
17118 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
17119     /**
17120      * @cfg {Boolean} singleSelect
17121      * True to allow selection of only one row at a time (defaults to false)
17122      */
17123     singleSelect : false,
17124
17125     // private
17126     initEvents : function(){
17127
17128         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
17129             this.grid.on("mousedown", this.handleMouseDown, this);
17130         }else{ // allow click to work like normal
17131             this.grid.on("rowclick", this.handleDragableRowClick, this);
17132         }
17133
17134         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
17135             "up" : function(e){
17136                 if(!e.shiftKey){
17137                     this.selectPrevious(e.shiftKey);
17138                 }else if(this.last !== false && this.lastActive !== false){
17139                     var last = this.last;
17140                     this.selectRange(this.last,  this.lastActive-1);
17141                     this.grid.getView().focusRow(this.lastActive);
17142                     if(last !== false){
17143                         this.last = last;
17144                     }
17145                 }else{
17146                     this.selectFirstRow();
17147                 }
17148                 this.fireEvent("afterselectionchange", this);
17149             },
17150             "down" : function(e){
17151                 if(!e.shiftKey){
17152                     this.selectNext(e.shiftKey);
17153                 }else if(this.last !== false && this.lastActive !== false){
17154                     var last = this.last;
17155                     this.selectRange(this.last,  this.lastActive+1);
17156                     this.grid.getView().focusRow(this.lastActive);
17157                     if(last !== false){
17158                         this.last = last;
17159                     }
17160                 }else{
17161                     this.selectFirstRow();
17162                 }
17163                 this.fireEvent("afterselectionchange", this);
17164             },
17165             scope: this
17166         });
17167
17168         var view = this.grid.view;
17169         view.on("refresh", this.onRefresh, this);
17170         view.on("rowupdated", this.onRowUpdated, this);
17171         view.on("rowremoved", this.onRemove, this);
17172     },
17173
17174     // private
17175     onRefresh : function(){
17176         var ds = this.grid.dataSource, i, v = this.grid.view;
17177         var s = this.selections;
17178         s.each(function(r){
17179             if((i = ds.indexOfId(r.id)) != -1){
17180                 v.onRowSelect(i);
17181             }else{
17182                 s.remove(r);
17183             }
17184         });
17185     },
17186
17187     // private
17188     onRemove : function(v, index, r){
17189         this.selections.remove(r);
17190     },
17191
17192     // private
17193     onRowUpdated : function(v, index, r){
17194         if(this.isSelected(r)){
17195             v.onRowSelect(index);
17196         }
17197     },
17198
17199     /**
17200      * Select records.
17201      * @param {Array} records The records to select
17202      * @param {Boolean} keepExisting (optional) True to keep existing selections
17203      */
17204     selectRecords : function(records, keepExisting){
17205         if(!keepExisting){
17206             this.clearSelections();
17207         }
17208         var ds = this.grid.dataSource;
17209         for(var i = 0, len = records.length; i < len; i++){
17210             this.selectRow(ds.indexOf(records[i]), true);
17211         }
17212     },
17213
17214     /**
17215      * Gets the number of selected rows.
17216      * @return {Number}
17217      */
17218     getCount : function(){
17219         return this.selections.length;
17220     },
17221
17222     /**
17223      * Selects the first row in the grid.
17224      */
17225     selectFirstRow : function(){
17226         this.selectRow(0);
17227     },
17228
17229     /**
17230      * Select the last row.
17231      * @param {Boolean} keepExisting (optional) True to keep existing selections
17232      */
17233     selectLastRow : function(keepExisting){
17234         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
17235     },
17236
17237     /**
17238      * Selects the row immediately following the last selected row.
17239      * @param {Boolean} keepExisting (optional) True to keep existing selections
17240      */
17241     selectNext : function(keepExisting){
17242         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
17243             this.selectRow(this.last+1, keepExisting);
17244             this.grid.getView().focusRow(this.last);
17245         }
17246     },
17247
17248     /**
17249      * Selects the row that precedes the last selected row.
17250      * @param {Boolean} keepExisting (optional) True to keep existing selections
17251      */
17252     selectPrevious : function(keepExisting){
17253         if(this.last){
17254             this.selectRow(this.last-1, keepExisting);
17255             this.grid.getView().focusRow(this.last);
17256         }
17257     },
17258
17259     /**
17260      * Returns the selected records
17261      * @return {Array} Array of selected records
17262      */
17263     getSelections : function(){
17264         return [].concat(this.selections.items);
17265     },
17266
17267     /**
17268      * Returns the first selected record.
17269      * @return {Record}
17270      */
17271     getSelected : function(){
17272         return this.selections.itemAt(0);
17273     },
17274
17275
17276     /**
17277      * Clears all selections.
17278      */
17279     clearSelections : function(fast){
17280         if(this.locked) return;
17281         if(fast !== true){
17282             var ds = this.grid.dataSource;
17283             var s = this.selections;
17284             s.each(function(r){
17285                 this.deselectRow(ds.indexOfId(r.id));
17286             }, this);
17287             s.clear();
17288         }else{
17289             this.selections.clear();
17290         }
17291         this.last = false;
17292     },
17293
17294
17295     /**
17296      * Selects all rows.
17297      */
17298     selectAll : function(){
17299         if(this.locked) return;
17300         this.selections.clear();
17301         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
17302             this.selectRow(i, true);
17303         }
17304     },
17305
17306     /**
17307      * Returns True if there is a selection.
17308      * @return {Boolean}
17309      */
17310     hasSelection : function(){
17311         return this.selections.length > 0;
17312     },
17313
17314     /**
17315      * Returns True if the specified row is selected.
17316      * @param {Number/Record} record The record or index of the record to check
17317      * @return {Boolean}
17318      */
17319     isSelected : function(index){
17320         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
17321         return (r && this.selections.key(r.id) ? true : false);
17322     },
17323
17324     /**
17325      * Returns True if the specified record id is selected.
17326      * @param {String} id The id of record to check
17327      * @return {Boolean}
17328      */
17329     isIdSelected : function(id){
17330         return (this.selections.key(id) ? true : false);
17331     },
17332
17333     // private
17334     handleMouseDown : function(e, t){
17335         var view = this.grid.getView(), rowIndex;
17336         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
17337             return;
17338         };
17339         if(e.shiftKey && this.last !== false){
17340             var last = this.last;
17341             this.selectRange(last, rowIndex, e.ctrlKey);
17342             this.last = last; // reset the last
17343             view.focusRow(rowIndex);
17344         }else{
17345             var isSelected = this.isSelected(rowIndex);
17346             if(e.button !== 0 && isSelected){
17347                 view.focusRow(rowIndex);
17348             }else if(e.ctrlKey && isSelected){
17349                 this.deselectRow(rowIndex);
17350             }else if(!isSelected){
17351                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
17352                 view.focusRow(rowIndex);
17353             }
17354         }
17355         this.fireEvent("afterselectionchange", this);
17356     },
17357     // private
17358     handleDragableRowClick :  function(grid, rowIndex, e) 
17359     {
17360         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
17361             this.selectRow(rowIndex, false);
17362             grid.view.focusRow(rowIndex);
17363              this.fireEvent("afterselectionchange", this);
17364         }
17365     },
17366     
17367     /**
17368      * Selects multiple rows.
17369      * @param {Array} rows Array of the indexes of the row to select
17370      * @param {Boolean} keepExisting (optional) True to keep existing selections
17371      */
17372     selectRows : function(rows, keepExisting){
17373         if(!keepExisting){
17374             this.clearSelections();
17375         }
17376         for(var i = 0, len = rows.length; i < len; i++){
17377             this.selectRow(rows[i], true);
17378         }
17379     },
17380
17381     /**
17382      * Selects a range of rows. All rows in between startRow and endRow are also selected.
17383      * @param {Number} startRow The index of the first row in the range
17384      * @param {Number} endRow The index of the last row in the range
17385      * @param {Boolean} keepExisting (optional) True to retain existing selections
17386      */
17387     selectRange : function(startRow, endRow, keepExisting){
17388         if(this.locked) return;
17389         if(!keepExisting){
17390             this.clearSelections();
17391         }
17392         if(startRow <= endRow){
17393             for(var i = startRow; i <= endRow; i++){
17394                 this.selectRow(i, true);
17395             }
17396         }else{
17397             for(var i = startRow; i >= endRow; i--){
17398                 this.selectRow(i, true);
17399             }
17400         }
17401     },
17402
17403     /**
17404      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
17405      * @param {Number} startRow The index of the first row in the range
17406      * @param {Number} endRow The index of the last row in the range
17407      */
17408     deselectRange : function(startRow, endRow, preventViewNotify){
17409         if(this.locked) return;
17410         for(var i = startRow; i <= endRow; i++){
17411             this.deselectRow(i, preventViewNotify);
17412         }
17413     },
17414
17415     /**
17416      * Selects a row.
17417      * @param {Number} row The index of the row to select
17418      * @param {Boolean} keepExisting (optional) True to keep existing selections
17419      */
17420     selectRow : function(index, keepExisting, preventViewNotify){
17421         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
17422         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
17423             if(!keepExisting || this.singleSelect){
17424                 this.clearSelections();
17425             }
17426             var r = this.grid.dataSource.getAt(index);
17427             this.selections.add(r);
17428             this.last = this.lastActive = index;
17429             if(!preventViewNotify){
17430                 this.grid.getView().onRowSelect(index);
17431             }
17432             this.fireEvent("rowselect", this, index, r);
17433             this.fireEvent("selectionchange", this);
17434         }
17435     },
17436
17437     /**
17438      * Deselects a row.
17439      * @param {Number} row The index of the row to deselect
17440      */
17441     deselectRow : function(index, preventViewNotify){
17442         if(this.locked) return;
17443         if(this.last == index){
17444             this.last = false;
17445         }
17446         if(this.lastActive == index){
17447             this.lastActive = false;
17448         }
17449         var r = this.grid.dataSource.getAt(index);
17450         this.selections.remove(r);
17451         if(!preventViewNotify){
17452             this.grid.getView().onRowDeselect(index);
17453         }
17454         this.fireEvent("rowdeselect", this, index);
17455         this.fireEvent("selectionchange", this);
17456     },
17457
17458     // private
17459     restoreLast : function(){
17460         if(this._last){
17461             this.last = this._last;
17462         }
17463     },
17464
17465     // private
17466     acceptsNav : function(row, col, cm){
17467         return !cm.isHidden(col) && cm.isCellEditable(col, row);
17468     },
17469
17470     // private
17471     onEditorKey : function(field, e){
17472         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
17473         if(k == e.TAB){
17474             e.stopEvent();
17475             ed.completeEdit();
17476             if(e.shiftKey){
17477                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
17478             }else{
17479                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
17480             }
17481         }else if(k == e.ENTER && !e.ctrlKey){
17482             e.stopEvent();
17483             ed.completeEdit();
17484             if(e.shiftKey){
17485                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
17486             }else{
17487                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
17488             }
17489         }else if(k == e.ESC){
17490             ed.cancelEdit();
17491         }
17492         if(newCell){
17493             g.startEditing(newCell[0], newCell[1]);
17494         }
17495     }
17496 });/*
17497  * - LGPL
17498  *
17499  * element
17500  * 
17501  */
17502
17503 /**
17504  * @class Roo.bootstrap.MessageBar
17505  * @extends Roo.bootstrap.Component
17506  * Bootstrap MessageBar class
17507  * @cfg {String} html contents of the MessageBar
17508  * @cfg {String} weight (info | success | warning | danger) default info
17509  * @cfg {String} beforeClass insert the bar before the given class
17510  * @cfg {Boolean} closable (true | false) default false
17511  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
17512  * 
17513  * @constructor
17514  * Create a new Element
17515  * @param {Object} config The config object
17516  */
17517
17518 Roo.bootstrap.MessageBar = function(config){
17519     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
17520 };
17521
17522 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
17523     
17524     html: '',
17525     weight: 'info',
17526     closable: false,
17527     fixed: false,
17528     beforeClass: 'bootstrap-sticky-wrap',
17529     
17530     getAutoCreate : function(){
17531         
17532         var cfg = {
17533             tag: 'div',
17534             cls: 'alert alert-dismissable alert-' + this.weight,
17535             cn: [
17536                 {
17537                     tag: 'span',
17538                     cls: 'message',
17539                     html: this.html || ''
17540                 }
17541             ]
17542         }
17543         
17544         if(this.fixed){
17545             cfg.cls += ' alert-messages-fixed';
17546         }
17547         
17548         if(this.closable){
17549             cfg.cn.push({
17550                 tag: 'button',
17551                 cls: 'close',
17552                 html: 'x'
17553             });
17554         }
17555         
17556         return cfg;
17557     },
17558     
17559     onRender : function(ct, position)
17560     {
17561         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17562         
17563         if(!this.el){
17564             var cfg = Roo.apply({},  this.getAutoCreate());
17565             cfg.id = Roo.id();
17566             
17567             if (this.cls) {
17568                 cfg.cls += ' ' + this.cls;
17569             }
17570             if (this.style) {
17571                 cfg.style = this.style;
17572             }
17573             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
17574             
17575             this.el.setVisibilityMode(Roo.Element.DISPLAY);
17576         }
17577         
17578         this.el.select('>button.close').on('click', this.hide, this);
17579         
17580     },
17581     
17582     show : function()
17583     {
17584         if (!this.rendered) {
17585             this.render();
17586         }
17587         
17588         this.el.show();
17589         
17590         this.fireEvent('show', this);
17591         
17592     },
17593     
17594     hide : function()
17595     {
17596         if (!this.rendered) {
17597             this.render();
17598         }
17599         
17600         this.el.hide();
17601         
17602         this.fireEvent('hide', this);
17603     },
17604     
17605     update : function()
17606     {
17607 //        var e = this.el.dom.firstChild;
17608 //        
17609 //        if(this.closable){
17610 //            e = e.nextSibling;
17611 //        }
17612 //        
17613 //        e.data = this.html || '';
17614
17615         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
17616     }
17617    
17618 });
17619
17620  
17621
17622