roojs-bootstrap.js
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * 
20  * @constructor
21  * Do not use directly - it does not do anything..
22  * @param {Object} config The config object
23  */
24
25
26
27 Roo.bootstrap.Component = function(config){
28     Roo.bootstrap.Component.superclass.constructor.call(this, config);
29 };
30
31 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
32     
33     
34     allowDomMove : false, // to stop relocations in parent onRender...
35     
36     cls : false,
37     
38     style : false,
39     
40     autoCreate : false,
41     
42     initEvents : function() {  },
43     
44     xattr : false,
45     
46     parentId : false,
47     
48     can_build_overlaid : true,
49     
50     dataId : false,
51     
52     name : false,
53     
54     parent: function() {
55         // returns the parent component..
56         return Roo.ComponentMgr.get(this.parentId)
57         
58         
59     },
60     
61     // private
62     onRender : function(ct, position)
63     {
64        // Roo.log("Call onRender: " + this.xtype);
65         
66         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
67         
68         if(this.el){
69             if (this.el.attr('xtype')) {
70                 this.el.attr('xtypex', this.el.attr('xtype'));
71                 this.el.dom.removeAttribute('xtype');
72                 
73                 this.initEvents();
74             }
75             
76             return;
77         }
78         
79          
80         
81         var cfg = Roo.apply({},  this.getAutoCreate());
82         cfg.id = Roo.id();
83         
84         // fill in the extra attributes 
85         if (this.xattr && typeof(this.xattr) =='object') {
86             for (var i in this.xattr) {
87                 cfg[i] = this.xattr[i];
88             }
89         }
90         
91         if(this.dataId){
92             cfg.dataId = this.dataId;
93         }
94         
95         if (this.cls) {
96             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
97         }
98         
99         if (this.style) { // fixme needs to support more complex style data.
100             cfg.style = this.style;
101         }
102         
103         if(this.name){
104             cfg.name = this.name;
105         }
106         
107         this.el = ct.createChild(cfg, position);
108         
109         if(this.tabIndex !== undefined){
110             this.el.dom.setAttribute('tabIndex', this.tabIndex);
111         }
112         this.initEvents();
113         
114         
115     },
116     
117     getChildContainer : function()
118     {
119         return this.el;
120     },
121     
122     
123     addxtype  : function(tree,cntr)
124     {
125         var cn = this;
126         
127         cn = Roo.factory(tree);
128            
129         cn.parentType = this.xtype; //??
130         cn.parentId = this.id;
131         
132         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
133         
134         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
135         
136         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
137         
138         var build_from_html =  Roo.XComponent.build_from_html;
139           
140         var is_body  = (tree.xtype == 'Body') ;
141           
142         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
143           
144         var self_cntr_el = Roo.get(this[cntr](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     titleEl : function()
962     {
963         if(!this.el || !this.panel.length || !this.header.length){
964             return;
965         }
966         
967         return this.el.select('.panel-title',true).first();
968     },
969     
970     setTitle : function(v)
971     {
972         var titleEl = this.titleEl();
973         
974         if(!titleEl){
975             return;
976         }
977         
978         titleEl.dom.innerHTML = v;
979     },
980     
981     getTitle : function()
982     {
983         
984         var titleEl = this.titleEl();
985         
986         if(!titleEl){
987             return '';
988         }
989         
990         return titleEl.dom.innerHTML;
991     }
992    
993 });
994
995  /*
996  * - LGPL
997  *
998  * image
999  * 
1000  */
1001
1002
1003 /**
1004  * @class Roo.bootstrap.Img
1005  * @extends Roo.bootstrap.Component
1006  * Bootstrap Img class
1007  * @cfg {Boolean} imgResponsive false | true
1008  * @cfg {String} border rounded | circle | thumbnail
1009  * @cfg {String} src image source
1010  * @cfg {String} alt image alternative text
1011  * @cfg {String} href a tag href
1012  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1013  * 
1014  * @constructor
1015  * Create a new Input
1016  * @param {Object} config The config object
1017  */
1018
1019 Roo.bootstrap.Img = function(config){
1020     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1021     
1022     this.addEvents({
1023         // img events
1024         /**
1025          * @event click
1026          * The img click event for the img.
1027          * @param {Roo.EventObject} e
1028          */
1029         "click" : true
1030     });
1031 };
1032
1033 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1034     
1035     imgResponsive: true,
1036     border: '',
1037     src: '',
1038     href: false,
1039     target: false,
1040
1041     getAutoCreate : function(){
1042         
1043         var cfg = {
1044             tag: 'img',
1045             cls: (this.imgResponsive) ? 'img-responsive' : '',
1046             html : null
1047         }
1048         
1049         cfg.html = this.html || cfg.html;
1050         
1051         cfg.src = this.src || cfg.src;
1052         
1053         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1054             cfg.cls += ' img-' + this.border;
1055         }
1056         
1057         if(this.alt){
1058             cfg.alt = this.alt;
1059         }
1060         
1061         if(this.href){
1062             var a = {
1063                 tag: 'a',
1064                 href: this.href,
1065                 cn: [
1066                     cfg
1067                 ]
1068             }
1069             
1070             if(this.target){
1071                 a.target = this.target;
1072             }
1073             
1074         }
1075         
1076         
1077         return (this.href) ? a : cfg;
1078     },
1079     
1080     initEvents: function() {
1081         
1082         if(!this.href){
1083             this.el.on('click', this.onClick, this);
1084         }
1085     },
1086     
1087     onClick : function(e)
1088     {
1089         Roo.log('img onclick');
1090         this.fireEvent('click', this, e);
1091     }
1092    
1093 });
1094
1095  /*
1096  * - LGPL
1097  *
1098  * image
1099  * 
1100  */
1101
1102
1103 /**
1104  * @class Roo.bootstrap.Link
1105  * @extends Roo.bootstrap.Component
1106  * Bootstrap Link Class
1107  * @cfg {String} alt image alternative text
1108  * @cfg {String} href a tag href
1109  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1110  * @cfg {String} html the content of the link.
1111  * @cfg {Boolean} preventDefault (true | false) default false
1112
1113  * 
1114  * @constructor
1115  * Create a new Input
1116  * @param {Object} config The config object
1117  */
1118
1119 Roo.bootstrap.Link = function(config){
1120     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1121     
1122     this.addEvents({
1123         // img events
1124         /**
1125          * @event click
1126          * The img click event for the img.
1127          * @param {Roo.EventObject} e
1128          */
1129         "click" : true
1130     });
1131 };
1132
1133 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1134     
1135     href: false,
1136     target: false,
1137     preventDefault: false,
1138
1139     getAutoCreate : function(){
1140         
1141         var cfg = {
1142             tag: 'a',
1143             html : this.html || 'html-missing'
1144         }
1145         
1146         
1147         if(this.alt){
1148             cfg.alt = this.alt;
1149         }
1150         cfg.href = this.href || '#';
1151         if(this.target){
1152             cfg.target = this.target;
1153         }
1154         
1155         return cfg;
1156     },
1157     
1158     initEvents: function() {
1159         
1160         if(!this.href || this.preventDefault){
1161             this.el.on('click', this.onClick, this);
1162         }
1163     },
1164     
1165     onClick : function(e)
1166     {
1167         if(this.preventDefault){
1168             e.preventDefault();
1169         }
1170         //Roo.log('img onclick');
1171         this.fireEvent('click', this, e);
1172     }
1173    
1174 });
1175
1176  /*
1177  * - LGPL
1178  *
1179  * header
1180  * 
1181  */
1182
1183 /**
1184  * @class Roo.bootstrap.Header
1185  * @extends Roo.bootstrap.Component
1186  * Bootstrap Header class
1187  * @cfg {String} html content of header
1188  * @cfg {Number} level (1|2|3|4|5|6) default 1
1189  * 
1190  * @constructor
1191  * Create a new Header
1192  * @param {Object} config The config object
1193  */
1194
1195
1196 Roo.bootstrap.Header  = function(config){
1197     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1198 };
1199
1200 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1201     
1202     //href : false,
1203     html : false,
1204     level : 1,
1205     
1206     
1207     
1208     getAutoCreate : function(){
1209         
1210         var cfg = {
1211             tag: 'h' + (1 *this.level),
1212             html: this.html || 'fill in html'
1213         } ;
1214         
1215         return cfg;
1216     }
1217    
1218 });
1219
1220  
1221
1222  /*
1223  * Based on:
1224  * Ext JS Library 1.1.1
1225  * Copyright(c) 2006-2007, Ext JS, LLC.
1226  *
1227  * Originally Released Under LGPL - original licence link has changed is not relivant.
1228  *
1229  * Fork - LGPL
1230  * <script type="text/javascript">
1231  */
1232  
1233 /**
1234  * @class Roo.bootstrap.MenuMgr
1235  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1236  * @singleton
1237  */
1238 Roo.bootstrap.MenuMgr = function(){
1239    var menus, active, groups = {}, attached = false, lastShow = new Date();
1240
1241    // private - called when first menu is created
1242    function init(){
1243        menus = {};
1244        active = new Roo.util.MixedCollection();
1245        Roo.get(document).addKeyListener(27, function(){
1246            if(active.length > 0){
1247                hideAll();
1248            }
1249        });
1250    }
1251
1252    // private
1253    function hideAll(){
1254        if(active && active.length > 0){
1255            var c = active.clone();
1256            c.each(function(m){
1257                m.hide();
1258            });
1259        }
1260    }
1261
1262    // private
1263    function onHide(m){
1264        active.remove(m);
1265        if(active.length < 1){
1266            Roo.get(document).un("mouseup", onMouseDown);
1267             
1268            attached = false;
1269        }
1270    }
1271
1272    // private
1273    function onShow(m){
1274        var last = active.last();
1275        lastShow = new Date();
1276        active.add(m);
1277        if(!attached){
1278           Roo.get(document).on("mouseup", onMouseDown);
1279            
1280            attached = true;
1281        }
1282        if(m.parentMenu){
1283           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1284           m.parentMenu.activeChild = m;
1285        }else if(last && last.isVisible()){
1286           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1287        }
1288    }
1289
1290    // private
1291    function onBeforeHide(m){
1292        if(m.activeChild){
1293            m.activeChild.hide();
1294        }
1295        if(m.autoHideTimer){
1296            clearTimeout(m.autoHideTimer);
1297            delete m.autoHideTimer;
1298        }
1299    }
1300
1301    // private
1302    function onBeforeShow(m){
1303        var pm = m.parentMenu;
1304        if(!pm && !m.allowOtherMenus){
1305            hideAll();
1306        }else if(pm && pm.activeChild && active != m){
1307            pm.activeChild.hide();
1308        }
1309    }
1310
1311    // private
1312    function onMouseDown(e){
1313         Roo.log("on MouseDown");
1314         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
1315            hideAll();
1316         }
1317         
1318         
1319    }
1320
1321    // private
1322    function onBeforeCheck(mi, state){
1323        if(state){
1324            var g = groups[mi.group];
1325            for(var i = 0, l = g.length; i < l; i++){
1326                if(g[i] != mi){
1327                    g[i].setChecked(false);
1328                }
1329            }
1330        }
1331    }
1332
1333    return {
1334
1335        /**
1336         * Hides all menus that are currently visible
1337         */
1338        hideAll : function(){
1339             hideAll();  
1340        },
1341
1342        // private
1343        register : function(menu){
1344            if(!menus){
1345                init();
1346            }
1347            menus[menu.id] = menu;
1348            menu.on("beforehide", onBeforeHide);
1349            menu.on("hide", onHide);
1350            menu.on("beforeshow", onBeforeShow);
1351            menu.on("show", onShow);
1352            var g = menu.group;
1353            if(g && menu.events["checkchange"]){
1354                if(!groups[g]){
1355                    groups[g] = [];
1356                }
1357                groups[g].push(menu);
1358                menu.on("checkchange", onCheck);
1359            }
1360        },
1361
1362         /**
1363          * Returns a {@link Roo.menu.Menu} object
1364          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1365          * be used to generate and return a new Menu instance.
1366          */
1367        get : function(menu){
1368            if(typeof menu == "string"){ // menu id
1369                return menus[menu];
1370            }else if(menu.events){  // menu instance
1371                return menu;
1372            }
1373            /*else if(typeof menu.length == 'number'){ // array of menu items?
1374                return new Roo.bootstrap.Menu({items:menu});
1375            }else{ // otherwise, must be a config
1376                return new Roo.bootstrap.Menu(menu);
1377            }
1378            */
1379            return false;
1380        },
1381
1382        // private
1383        unregister : function(menu){
1384            delete menus[menu.id];
1385            menu.un("beforehide", onBeforeHide);
1386            menu.un("hide", onHide);
1387            menu.un("beforeshow", onBeforeShow);
1388            menu.un("show", onShow);
1389            var g = menu.group;
1390            if(g && menu.events["checkchange"]){
1391                groups[g].remove(menu);
1392                menu.un("checkchange", onCheck);
1393            }
1394        },
1395
1396        // private
1397        registerCheckable : function(menuItem){
1398            var g = menuItem.group;
1399            if(g){
1400                if(!groups[g]){
1401                    groups[g] = [];
1402                }
1403                groups[g].push(menuItem);
1404                menuItem.on("beforecheckchange", onBeforeCheck);
1405            }
1406        },
1407
1408        // private
1409        unregisterCheckable : function(menuItem){
1410            var g = menuItem.group;
1411            if(g){
1412                groups[g].remove(menuItem);
1413                menuItem.un("beforecheckchange", onBeforeCheck);
1414            }
1415        }
1416    };
1417 }();/*
1418  * - LGPL
1419  *
1420  * menu
1421  * 
1422  */
1423
1424 /**
1425  * @class Roo.bootstrap.Menu
1426  * @extends Roo.bootstrap.Component
1427  * Bootstrap Menu class - container for MenuItems
1428  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1429  * 
1430  * @constructor
1431  * Create a new Menu
1432  * @param {Object} config The config object
1433  */
1434
1435
1436 Roo.bootstrap.Menu = function(config){
1437     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1438     if (this.registerMenu) {
1439         Roo.bootstrap.MenuMgr.register(this);
1440     }
1441     this.addEvents({
1442         /**
1443          * @event beforeshow
1444          * Fires before this menu is displayed
1445          * @param {Roo.menu.Menu} this
1446          */
1447         beforeshow : true,
1448         /**
1449          * @event beforehide
1450          * Fires before this menu is hidden
1451          * @param {Roo.menu.Menu} this
1452          */
1453         beforehide : true,
1454         /**
1455          * @event show
1456          * Fires after this menu is displayed
1457          * @param {Roo.menu.Menu} this
1458          */
1459         show : true,
1460         /**
1461          * @event hide
1462          * Fires after this menu is hidden
1463          * @param {Roo.menu.Menu} this
1464          */
1465         hide : true,
1466         /**
1467          * @event click
1468          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1469          * @param {Roo.menu.Menu} this
1470          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1471          * @param {Roo.EventObject} e
1472          */
1473         click : true,
1474         /**
1475          * @event mouseover
1476          * Fires when the mouse is hovering over this menu
1477          * @param {Roo.menu.Menu} this
1478          * @param {Roo.EventObject} e
1479          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1480          */
1481         mouseover : true,
1482         /**
1483          * @event mouseout
1484          * Fires when the mouse exits this menu
1485          * @param {Roo.menu.Menu} this
1486          * @param {Roo.EventObject} e
1487          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1488          */
1489         mouseout : true,
1490         /**
1491          * @event itemclick
1492          * Fires when a menu item contained in this menu is clicked
1493          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1494          * @param {Roo.EventObject} e
1495          */
1496         itemclick: true
1497     });
1498     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1499 };
1500
1501 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1502     
1503    /// html : false,
1504     //align : '',
1505     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1506     type: false,
1507     /**
1508      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1509      */
1510     registerMenu : true,
1511     
1512     menuItems :false, // stores the menu items..
1513     
1514     hidden:true,
1515     
1516     parentMenu : false,
1517     
1518     getChildContainer : function() {
1519         return this.el;  
1520     },
1521     
1522     getAutoCreate : function(){
1523          
1524         //if (['right'].indexOf(this.align)!==-1) {
1525         //    cfg.cn[1].cls += ' pull-right'
1526         //}
1527         
1528         
1529         var cfg = {
1530             tag : 'ul',
1531             cls : 'dropdown-menu' ,
1532             style : 'z-index:1000'
1533             
1534         }
1535         
1536         if (this.type === 'submenu') {
1537             cfg.cls = 'submenu active';
1538         }
1539         if (this.type === 'treeview') {
1540             cfg.cls = 'treeview-menu';
1541         }
1542         
1543         return cfg;
1544     },
1545     initEvents : function() {
1546         
1547        // Roo.log("ADD event");
1548        // Roo.log(this.triggerEl.dom);
1549         this.triggerEl.on('click', this.onTriggerPress, this);
1550         this.triggerEl.addClass('dropdown-toggle');
1551         this.el.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
1552
1553         this.el.on("mouseover", this.onMouseOver, this);
1554         this.el.on("mouseout", this.onMouseOut, this);
1555         
1556         
1557     },
1558     findTargetItem : function(e){
1559         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
1560         if(!t){
1561             return false;
1562         }
1563         //Roo.log(t);         Roo.log(t.id);
1564         if(t && t.id){
1565             //Roo.log(this.menuitems);
1566             return this.menuitems.get(t.id);
1567             
1568             //return this.items.get(t.menuItemId);
1569         }
1570         
1571         return false;
1572     },
1573     onClick : function(e){
1574         Roo.log("menu.onClick");
1575         var t = this.findTargetItem(e);
1576         if(!t){
1577             return;
1578         }
1579         Roo.log(e);
1580         /*
1581         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
1582             if(t == this.activeItem && t.shouldDeactivate(e)){
1583                 this.activeItem.deactivate();
1584                 delete this.activeItem;
1585                 return;
1586             }
1587             if(t.canActivate){
1588                 this.setActiveItem(t, true);
1589             }
1590             return;
1591             
1592             
1593         }
1594         */
1595         Roo.log('pass click event');
1596         
1597         t.onClick(e);
1598         
1599         this.fireEvent("click", this, t, e);
1600         
1601         this.hide();
1602     },
1603      onMouseOver : function(e){
1604         var t  = this.findTargetItem(e);
1605         //Roo.log(t);
1606         //if(t){
1607         //    if(t.canActivate && !t.disabled){
1608         //        this.setActiveItem(t, true);
1609         //    }
1610         //}
1611         
1612         this.fireEvent("mouseover", this, e, t);
1613     },
1614     isVisible : function(){
1615         return !this.hidden;
1616     },
1617      onMouseOut : function(e){
1618         var t  = this.findTargetItem(e);
1619         
1620         //if(t ){
1621         //    if(t == this.activeItem && t.shouldDeactivate(e)){
1622         //        this.activeItem.deactivate();
1623         //        delete this.activeItem;
1624         //    }
1625         //}
1626         this.fireEvent("mouseout", this, e, t);
1627     },
1628     
1629     
1630     /**
1631      * Displays this menu relative to another element
1632      * @param {String/HTMLElement/Roo.Element} element The element to align to
1633      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
1634      * the element (defaults to this.defaultAlign)
1635      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1636      */
1637     show : function(el, pos, parentMenu){
1638         this.parentMenu = parentMenu;
1639         if(!this.el){
1640             this.render();
1641         }
1642         this.fireEvent("beforeshow", this);
1643         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
1644     },
1645      /**
1646      * Displays this menu at a specific xy position
1647      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
1648      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1649      */
1650     showAt : function(xy, parentMenu, /* private: */_e){
1651         this.parentMenu = parentMenu;
1652         if(!this.el){
1653             this.render();
1654         }
1655         if(_e !== false){
1656             this.fireEvent("beforeshow", this);
1657             
1658             //xy = this.el.adjustForConstraints(xy);
1659         }
1660         //this.el.setXY(xy);
1661         //this.el.show();
1662         this.hideMenuItems();
1663         this.hidden = false;
1664         this.triggerEl.addClass('open');
1665         this.focus();
1666         this.fireEvent("show", this);
1667     },
1668     
1669     focus : function(){
1670         return;
1671         if(!this.hidden){
1672             this.doFocus.defer(50, this);
1673         }
1674     },
1675
1676     doFocus : function(){
1677         if(!this.hidden){
1678             this.focusEl.focus();
1679         }
1680     },
1681
1682     /**
1683      * Hides this menu and optionally all parent menus
1684      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
1685      */
1686     hide : function(deep){
1687         
1688         this.hideMenuItems();
1689         if(this.el && this.isVisible()){
1690             this.fireEvent("beforehide", this);
1691             if(this.activeItem){
1692                 this.activeItem.deactivate();
1693                 this.activeItem = null;
1694             }
1695             this.triggerEl.removeClass('open');;
1696             this.hidden = true;
1697             this.fireEvent("hide", this);
1698         }
1699         if(deep === true && this.parentMenu){
1700             this.parentMenu.hide(true);
1701         }
1702     },
1703     
1704     onTriggerPress  : function(e)
1705     {
1706         
1707         Roo.log('trigger press');
1708         //Roo.log(e.getTarget());
1709        // Roo.log(this.triggerEl.dom);
1710         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
1711             return;
1712         }
1713         if (this.isVisible()) {
1714             Roo.log('hide');
1715             this.hide();
1716         } else {
1717             this.show(this.triggerEl, false, false);
1718         }
1719         
1720         
1721     },
1722     
1723          
1724        
1725     
1726     hideMenuItems : function()
1727     {
1728         //$(backdrop).remove()
1729         Roo.select('.open',true).each(function(aa) {
1730             
1731             aa.removeClass('open');
1732           //var parent = getParent($(this))
1733           //var relatedTarget = { relatedTarget: this }
1734           
1735            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
1736           //if (e.isDefaultPrevented()) return
1737            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
1738         })
1739     },
1740     addxtypeChild : function (tree, cntr) {
1741         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
1742           
1743         this.menuitems.add(comp);
1744         return comp;
1745
1746     },
1747     getEl : function()
1748     {
1749         Roo.log(this.el);
1750         return this.el;
1751     }
1752 });
1753
1754  
1755  /*
1756  * - LGPL
1757  *
1758  * menu item
1759  * 
1760  */
1761
1762
1763 /**
1764  * @class Roo.bootstrap.MenuItem
1765  * @extends Roo.bootstrap.Component
1766  * Bootstrap MenuItem class
1767  * @cfg {String} html the menu label
1768  * @cfg {String} href the link
1769  * @cfg {Boolean} preventDefault (true | false) default true
1770  * 
1771  * 
1772  * @constructor
1773  * Create a new MenuItem
1774  * @param {Object} config The config object
1775  */
1776
1777
1778 Roo.bootstrap.MenuItem = function(config){
1779     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
1780     this.addEvents({
1781         // raw events
1782         /**
1783          * @event click
1784          * The raw click event for the entire grid.
1785          * @param {Roo.EventObject} e
1786          */
1787         "click" : true
1788     });
1789 };
1790
1791 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
1792     
1793     href : false,
1794     html : false,
1795     preventDefault: true,
1796     
1797     getAutoCreate : function(){
1798         var cfg= {
1799             tag: 'li',
1800             cls: 'dropdown-menu-item',
1801             cn: [
1802                     {
1803                         tag : 'a',
1804                         href : '#',
1805                         html : 'Link'
1806                     }
1807                 ]
1808         };
1809         if (this.parent().type == 'treeview') {
1810             cfg.cls = 'treeview-menu';
1811         }
1812         
1813         cfg.cn[0].href = this.href || cfg.cn[0].href ;
1814         cfg.cn[0].html = this.html || cfg.cn[0].html ;
1815         return cfg;
1816     },
1817     
1818     initEvents: function() {
1819         
1820         //this.el.select('a').on('click', this.onClick, this);
1821         
1822     },
1823     onClick : function(e)
1824     {
1825         Roo.log('item on click ');
1826         //if(this.preventDefault){
1827         //    e.preventDefault();
1828         //}
1829         //this.parent().hideMenuItems();
1830         
1831         this.fireEvent('click', this, e);
1832     },
1833     getEl : function()
1834     {
1835         return this.el;
1836     }
1837 });
1838
1839  
1840
1841  /*
1842  * - LGPL
1843  *
1844  * menu separator
1845  * 
1846  */
1847
1848
1849 /**
1850  * @class Roo.bootstrap.MenuSeparator
1851  * @extends Roo.bootstrap.Component
1852  * Bootstrap MenuSeparator class
1853  * 
1854  * @constructor
1855  * Create a new MenuItem
1856  * @param {Object} config The config object
1857  */
1858
1859
1860 Roo.bootstrap.MenuSeparator = function(config){
1861     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
1862 };
1863
1864 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
1865     
1866     getAutoCreate : function(){
1867         var cfg = {
1868             cls: 'divider',
1869             tag : 'li'
1870         };
1871         
1872         return cfg;
1873     }
1874    
1875 });
1876
1877  
1878
1879  
1880 /*
1881 <div class="modal fade">
1882   <div class="modal-dialog">
1883     <div class="modal-content">
1884       <div class="modal-header">
1885         <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
1886         <h4 class="modal-title">Modal title</h4>
1887       </div>
1888       <div class="modal-body">
1889         <p>One fine body&hellip;</p>
1890       </div>
1891       <div class="modal-footer">
1892         <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
1893         <button type="button" class="btn btn-primary">Save changes</button>
1894       </div>
1895     </div><!-- /.modal-content -->
1896   </div><!-- /.modal-dialog -->
1897 </div><!-- /.modal -->
1898 */
1899 /*
1900  * - LGPL
1901  *
1902  * page contgainer.
1903  * 
1904  */
1905
1906 /**
1907  * @class Roo.bootstrap.Modal
1908  * @extends Roo.bootstrap.Component
1909  * Bootstrap Modal class
1910  * @cfg {String} title Title of dialog
1911  * @cfg {Boolean} specificTitle (true|false) default false
1912  * @cfg {Array} buttons Array of buttons or standard button set..
1913  * @cfg {String} buttonPosition (left|right|center) default right
1914  * 
1915  * @constructor
1916  * Create a new Modal Dialog
1917  * @param {Object} config The config object
1918  */
1919
1920 Roo.bootstrap.Modal = function(config){
1921     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
1922     this.addEvents({
1923         // raw events
1924         /**
1925          * @event btnclick
1926          * The raw btnclick event for the button
1927          * @param {Roo.EventObject} e
1928          */
1929         "btnclick" : true
1930     });
1931     this.buttons = this.buttons || [];
1932 };
1933
1934 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
1935     
1936     title : 'test dialog',
1937    
1938     buttons : false,
1939     
1940     // set on load...
1941     body:  false,
1942     
1943     specificTitle: false,
1944     
1945     buttonPosition: 'right',
1946     
1947     onRender : function(ct, position)
1948     {
1949         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
1950      
1951         if(!this.el){
1952             var cfg = Roo.apply({},  this.getAutoCreate());
1953             cfg.id = Roo.id();
1954             //if(!cfg.name){
1955             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
1956             //}
1957             //if (!cfg.name.length) {
1958             //    delete cfg.name;
1959            // }
1960             if (this.cls) {
1961                 cfg.cls += ' ' + this.cls;
1962             }
1963             if (this.style) {
1964                 cfg.style = this.style;
1965             }
1966             this.el = Roo.get(document.body).createChild(cfg, position);
1967         }
1968         //var type = this.el.dom.type;
1969         
1970         if(this.tabIndex !== undefined){
1971             this.el.dom.setAttribute('tabIndex', this.tabIndex);
1972         }
1973         
1974         
1975         
1976         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
1977         this.maskEl.enableDisplayMode("block");
1978         this.maskEl.hide();
1979         //this.el.addClass("x-dlg-modal");
1980     
1981         if (this.buttons.length) {
1982             Roo.each(this.buttons, function(bb) {
1983                 b = Roo.apply({}, bb);
1984                 b.xns = b.xns || Roo.bootstrap;
1985                 b.xtype = b.xtype || 'Button';
1986                 if (typeof(b.listeners) == 'undefined') {
1987                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
1988                 }
1989                 
1990                 var btn = Roo.factory(b);
1991                 
1992                 btn.onRender(this.el.select('.modal-footer div').first());
1993                 
1994             },this);
1995         }
1996         // render the children.
1997         var nitems = [];
1998         
1999         if(typeof(this.items) != 'undefined'){
2000             var items = this.items;
2001             delete this.items;
2002
2003             for(var i =0;i < items.length;i++) {
2004                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2005             }
2006         }
2007         
2008         this.items = nitems;
2009         
2010         this.body = this.el.select('.modal-body',true).first();
2011         this.close = this.el.select('.modal-header .close', true).first();
2012         this.footer = this.el.select('.modal-footer',true).first();
2013         this.initEvents();
2014         //this.el.addClass([this.fieldClass, this.cls]);
2015         
2016     },
2017     getAutoCreate : function(){
2018         
2019         
2020         var bdy = {
2021                 cls : 'modal-body',
2022                 html : this.html || ''
2023         };
2024         
2025         var title = {
2026             tag: 'h4',
2027             cls : 'modal-title',
2028             html : this.title
2029         };
2030         
2031         if(this.specificTitle){
2032             title = this.title;
2033         };
2034         
2035         return modal = {
2036             cls: "modal fade",
2037             style : 'display: none',
2038             cn : [
2039                 {
2040                     cls: "modal-dialog",
2041                     cn : [
2042                         {
2043                             cls : "modal-content",
2044                             cn : [
2045                                 {
2046                                     cls : 'modal-header',
2047                                     cn : [
2048                                         {
2049                                             tag: 'button',
2050                                             cls : 'close',
2051                                             html : '&times'
2052                                         },
2053                                         title
2054                                     ]
2055                                 },
2056                                 bdy,
2057                                 {
2058                                     cls : 'modal-footer',
2059                                     cn : [
2060                                         {
2061                                             tag: 'div',
2062                                             cls: 'btn-' + this.buttonPosition
2063                                         }
2064                                     ]
2065                                     
2066                                 }
2067                                 
2068                                 
2069                             ]
2070                             
2071                         }
2072                     ]
2073                         
2074                 }
2075             ]
2076             
2077             
2078         };
2079           
2080     },
2081     getChildContainer : function() {
2082          
2083          return this.el.select('.modal-body',true).first();
2084         
2085     },
2086     getButtonContainer : function() {
2087          return this.el.select('.modal-footer div',true).first();
2088         
2089     },
2090     initEvents : function()
2091     {
2092         this.el.select('.modal-header .close').on('click', this.hide, this);
2093 //        
2094 //        this.addxtype(this);
2095     },
2096     show : function() {
2097         
2098         if (!this.rendered) {
2099             this.render();
2100         }
2101        
2102         this.el.addClass('on');
2103         this.el.removeClass('fade');
2104         this.el.setStyle('display', 'block');
2105         Roo.get(document.body).addClass("x-body-masked");
2106         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2107         this.maskEl.show();
2108         this.el.setStyle('zIndex', '10001');
2109         this.fireEvent('show', this);
2110         
2111         
2112     },
2113     hide : function()
2114     {
2115         Roo.log('Modal hide?!');
2116         this.maskEl.hide();
2117         Roo.get(document.body).removeClass("x-body-masked");
2118         this.el.removeClass('on');
2119         this.el.addClass('fade');
2120         this.el.setStyle('display', 'none');
2121         this.fireEvent('hide', this);
2122     },
2123     
2124     addButton : function(str, cb)
2125     {
2126          
2127         
2128         var b = Roo.apply({}, { html : str } );
2129         b.xns = b.xns || Roo.bootstrap;
2130         b.xtype = b.xtype || 'Button';
2131         if (typeof(b.listeners) == 'undefined') {
2132             b.listeners = { click : cb.createDelegate(this)  };
2133         }
2134         
2135         var btn = Roo.factory(b);
2136            
2137         btn.onRender(this.el.select('.modal-footer div').first());
2138         
2139         return btn;   
2140        
2141     },
2142     
2143     setDefaultButton : function(btn)
2144     {
2145         //this.el.select('.modal-footer').()
2146     },
2147     resizeTo: function(w,h)
2148     {
2149         // skip..
2150     },
2151     setContentSize  : function(w, h)
2152     {
2153         
2154     },
2155     onButtonClick: function(btn,e)
2156     {
2157         //Roo.log([a,b,c]);
2158         this.fireEvent('btnclick', btn.name, e);
2159     },
2160     setTitle: function(str) {
2161         this.el.select('.modal-title',true).first().dom.innerHTML = str;
2162         
2163     }
2164 });
2165
2166
2167 Roo.apply(Roo.bootstrap.Modal,  {
2168     /**
2169          * Button config that displays a single OK button
2170          * @type Object
2171          */
2172         OK :  [{
2173             name : 'ok',
2174             weight : 'primary',
2175             html : 'OK'
2176         }], 
2177         /**
2178          * Button config that displays Yes and No buttons
2179          * @type Object
2180          */
2181         YESNO : [
2182             {
2183                 name  : 'no',
2184                 html : 'No'
2185             },
2186             {
2187                 name  :'yes',
2188                 weight : 'primary',
2189                 html : 'Yes'
2190             }
2191         ],
2192         
2193         /**
2194          * Button config that displays OK and Cancel buttons
2195          * @type Object
2196          */
2197         OKCANCEL : [
2198             {
2199                name : 'cancel',
2200                 html : 'Cancel'
2201             },
2202             {
2203                 name : 'ok',
2204                 weight : 'primary',
2205                 html : 'OK'
2206             }
2207         ],
2208         /**
2209          * Button config that displays Yes, No and Cancel buttons
2210          * @type Object
2211          */
2212         YESNOCANCEL : [
2213             {
2214                 name : 'yes',
2215                 weight : 'primary',
2216                 html : 'Yes'
2217             },
2218             {
2219                 name : 'no',
2220                 html : 'No'
2221             },
2222             {
2223                 name : 'cancel',
2224                 html : 'Cancel'
2225             }
2226         ]
2227 });
2228  /*
2229  * - LGPL
2230  *
2231  * messagebox - can be used as a replace
2232  * 
2233  */
2234 /**
2235  * @class Roo.MessageBox
2236  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2237  * Example usage:
2238  *<pre><code>
2239 // Basic alert:
2240 Roo.Msg.alert('Status', 'Changes saved successfully.');
2241
2242 // Prompt for user data:
2243 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2244     if (btn == 'ok'){
2245         // process text value...
2246     }
2247 });
2248
2249 // Show a dialog using config options:
2250 Roo.Msg.show({
2251    title:'Save Changes?',
2252    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2253    buttons: Roo.Msg.YESNOCANCEL,
2254    fn: processResult,
2255    animEl: 'elId'
2256 });
2257 </code></pre>
2258  * @singleton
2259  */
2260 Roo.bootstrap.MessageBox = function(){
2261     var dlg, opt, mask, waitTimer;
2262     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2263     var buttons, activeTextEl, bwidth;
2264
2265     
2266     // private
2267     var handleButton = function(button){
2268         dlg.hide();
2269         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2270     };
2271
2272     // private
2273     var handleHide = function(){
2274         if(opt && opt.cls){
2275             dlg.el.removeClass(opt.cls);
2276         }
2277         //if(waitTimer){
2278         //    Roo.TaskMgr.stop(waitTimer);
2279         //    waitTimer = null;
2280         //}
2281     };
2282
2283     // private
2284     var updateButtons = function(b){
2285         var width = 0;
2286         if(!b){
2287             buttons["ok"].hide();
2288             buttons["cancel"].hide();
2289             buttons["yes"].hide();
2290             buttons["no"].hide();
2291             //dlg.footer.dom.style.display = 'none';
2292             return width;
2293         }
2294         dlg.footer.dom.style.display = '';
2295         for(var k in buttons){
2296             if(typeof buttons[k] != "function"){
2297                 if(b[k]){
2298                     buttons[k].show();
2299                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2300                     width += buttons[k].el.getWidth()+15;
2301                 }else{
2302                     buttons[k].hide();
2303                 }
2304             }
2305         }
2306         return width;
2307     };
2308
2309     // private
2310     var handleEsc = function(d, k, e){
2311         if(opt && opt.closable !== false){
2312             dlg.hide();
2313         }
2314         if(e){
2315             e.stopEvent();
2316         }
2317     };
2318
2319     return {
2320         /**
2321          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2322          * @return {Roo.BasicDialog} The BasicDialog element
2323          */
2324         getDialog : function(){
2325            if(!dlg){
2326                 dlg = new Roo.bootstrap.Modal( {
2327                     //draggable: true,
2328                     //resizable:false,
2329                     //constraintoviewport:false,
2330                     //fixedcenter:true,
2331                     //collapsible : false,
2332                     //shim:true,
2333                     //modal: true,
2334                   //  width:400,
2335                   //  height:100,
2336                     //buttonAlign:"center",
2337                     closeClick : function(){
2338                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2339                             handleButton("no");
2340                         }else{
2341                             handleButton("cancel");
2342                         }
2343                     }
2344                 });
2345                 dlg.render();
2346                 dlg.on("hide", handleHide);
2347                 mask = dlg.mask;
2348                 //dlg.addKeyListener(27, handleEsc);
2349                 buttons = {};
2350                 this.buttons = buttons;
2351                 var bt = this.buttonText;
2352                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2353                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2354                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2355                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2356                 Roo.log(buttons)
2357                 bodyEl = dlg.body.createChild({
2358
2359                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2360                         '<textarea class="roo-mb-textarea"></textarea>' +
2361                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
2362                 });
2363                 msgEl = bodyEl.dom.firstChild;
2364                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2365                 textboxEl.enableDisplayMode();
2366                 textboxEl.addKeyListener([10,13], function(){
2367                     if(dlg.isVisible() && opt && opt.buttons){
2368                         if(opt.buttons.ok){
2369                             handleButton("ok");
2370                         }else if(opt.buttons.yes){
2371                             handleButton("yes");
2372                         }
2373                     }
2374                 });
2375                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2376                 textareaEl.enableDisplayMode();
2377                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2378                 progressEl.enableDisplayMode();
2379                 var pf = progressEl.dom.firstChild;
2380                 if (pf) {
2381                     pp = Roo.get(pf.firstChild);
2382                     pp.setHeight(pf.offsetHeight);
2383                 }
2384                 
2385             }
2386             return dlg;
2387         },
2388
2389         /**
2390          * Updates the message box body text
2391          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2392          * the XHTML-compliant non-breaking space character '&amp;#160;')
2393          * @return {Roo.MessageBox} This message box
2394          */
2395         updateText : function(text){
2396             if(!dlg.isVisible() && !opt.width){
2397                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2398             }
2399             msgEl.innerHTML = text || '&#160;';
2400       
2401             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2402             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2403             var w = Math.max(
2404                     Math.min(opt.width || cw , this.maxWidth), 
2405                     Math.max(opt.minWidth || this.minWidth, bwidth)
2406             );
2407             if(opt.prompt){
2408                 activeTextEl.setWidth(w);
2409             }
2410             if(dlg.isVisible()){
2411                 dlg.fixedcenter = false;
2412             }
2413             // to big, make it scroll. = But as usual stupid IE does not support
2414             // !important..
2415             
2416             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2417                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2418                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2419             } else {
2420                 bodyEl.dom.style.height = '';
2421                 bodyEl.dom.style.overflowY = '';
2422             }
2423             if (cw > w) {
2424                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2425             } else {
2426                 bodyEl.dom.style.overflowX = '';
2427             }
2428             
2429             dlg.setContentSize(w, bodyEl.getHeight());
2430             if(dlg.isVisible()){
2431                 dlg.fixedcenter = true;
2432             }
2433             return this;
2434         },
2435
2436         /**
2437          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
2438          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2439          * @param {Number} value Any number between 0 and 1 (e.g., .5)
2440          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2441          * @return {Roo.MessageBox} This message box
2442          */
2443         updateProgress : function(value, text){
2444             if(text){
2445                 this.updateText(text);
2446             }
2447             if (pp) { // weird bug on my firefox - for some reason this is not defined
2448                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2449             }
2450             return this;
2451         },        
2452
2453         /**
2454          * Returns true if the message box is currently displayed
2455          * @return {Boolean} True if the message box is visible, else false
2456          */
2457         isVisible : function(){
2458             return dlg && dlg.isVisible();  
2459         },
2460
2461         /**
2462          * Hides the message box if it is displayed
2463          */
2464         hide : function(){
2465             if(this.isVisible()){
2466                 dlg.hide();
2467             }  
2468         },
2469
2470         /**
2471          * Displays a new message box, or reinitializes an existing message box, based on the config options
2472          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2473          * The following config object properties are supported:
2474          * <pre>
2475 Property    Type             Description
2476 ----------  ---------------  ------------------------------------------------------------------------------------
2477 animEl            String/Element   An id or Element from which the message box should animate as it opens and
2478                                    closes (defaults to undefined)
2479 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
2480                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
2481 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
2482                                    progress and wait dialogs will ignore this property and always hide the
2483                                    close button as they can only be closed programmatically.
2484 cls               String           A custom CSS class to apply to the message box element
2485 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
2486                                    displayed (defaults to 75)
2487 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
2488                                    function will be btn (the name of the button that was clicked, if applicable,
2489                                    e.g. "ok"), and text (the value of the active text field, if applicable).
2490                                    Progress and wait dialogs will ignore this option since they do not respond to
2491                                    user actions and can only be closed programmatically, so any required function
2492                                    should be called by the same code after it closes the dialog.
2493 icon              String           A CSS class that provides a background image to be used as an icon for
2494                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
2495 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
2496 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
2497 modal             Boolean          False to allow user interaction with the page while the message box is
2498                                    displayed (defaults to true)
2499 msg               String           A string that will replace the existing message box body text (defaults
2500                                    to the XHTML-compliant non-breaking space character '&#160;')
2501 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
2502 progress          Boolean          True to display a progress bar (defaults to false)
2503 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
2504 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
2505 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
2506 title             String           The title text
2507 value             String           The string value to set into the active textbox element if displayed
2508 wait              Boolean          True to display a progress bar (defaults to false)
2509 width             Number           The width of the dialog in pixels
2510 </pre>
2511          *
2512          * Example usage:
2513          * <pre><code>
2514 Roo.Msg.show({
2515    title: 'Address',
2516    msg: 'Please enter your address:',
2517    width: 300,
2518    buttons: Roo.MessageBox.OKCANCEL,
2519    multiline: true,
2520    fn: saveAddress,
2521    animEl: 'addAddressBtn'
2522 });
2523 </code></pre>
2524          * @param {Object} config Configuration options
2525          * @return {Roo.MessageBox} This message box
2526          */
2527         show : function(options)
2528         {
2529             
2530             // this causes nightmares if you show one dialog after another
2531             // especially on callbacks..
2532              
2533             if(this.isVisible()){
2534                 
2535                 this.hide();
2536                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
2537                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
2538                 Roo.log("New Dialog Message:" +  options.msg )
2539                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
2540                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
2541                 
2542             }
2543             var d = this.getDialog();
2544             opt = options;
2545             d.setTitle(opt.title || "&#160;");
2546             d.close.setDisplayed(opt.closable !== false);
2547             activeTextEl = textboxEl;
2548             opt.prompt = opt.prompt || (opt.multiline ? true : false);
2549             if(opt.prompt){
2550                 if(opt.multiline){
2551                     textboxEl.hide();
2552                     textareaEl.show();
2553                     textareaEl.setHeight(typeof opt.multiline == "number" ?
2554                         opt.multiline : this.defaultTextHeight);
2555                     activeTextEl = textareaEl;
2556                 }else{
2557                     textboxEl.show();
2558                     textareaEl.hide();
2559                 }
2560             }else{
2561                 textboxEl.hide();
2562                 textareaEl.hide();
2563             }
2564             progressEl.setDisplayed(opt.progress === true);
2565             this.updateProgress(0);
2566             activeTextEl.dom.value = opt.value || "";
2567             if(opt.prompt){
2568                 dlg.setDefaultButton(activeTextEl);
2569             }else{
2570                 var bs = opt.buttons;
2571                 var db = null;
2572                 if(bs && bs.ok){
2573                     db = buttons["ok"];
2574                 }else if(bs && bs.yes){
2575                     db = buttons["yes"];
2576                 }
2577                 dlg.setDefaultButton(db);
2578             }
2579             bwidth = updateButtons(opt.buttons);
2580             this.updateText(opt.msg);
2581             if(opt.cls){
2582                 d.el.addClass(opt.cls);
2583             }
2584             d.proxyDrag = opt.proxyDrag === true;
2585             d.modal = opt.modal !== false;
2586             d.mask = opt.modal !== false ? mask : false;
2587             if(!d.isVisible()){
2588                 // force it to the end of the z-index stack so it gets a cursor in FF
2589                 document.body.appendChild(dlg.el.dom);
2590                 d.animateTarget = null;
2591                 d.show(options.animEl);
2592             }
2593             return this;
2594         },
2595
2596         /**
2597          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
2598          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
2599          * and closing the message box when the process is complete.
2600          * @param {String} title The title bar text
2601          * @param {String} msg The message box body text
2602          * @return {Roo.MessageBox} This message box
2603          */
2604         progress : function(title, msg){
2605             this.show({
2606                 title : title,
2607                 msg : msg,
2608                 buttons: false,
2609                 progress:true,
2610                 closable:false,
2611                 minWidth: this.minProgressWidth,
2612                 modal : true
2613             });
2614             return this;
2615         },
2616
2617         /**
2618          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
2619          * If a callback function is passed it will be called after the user clicks the button, and the
2620          * id of the button that was clicked will be passed as the only parameter to the callback
2621          * (could also be the top-right close button).
2622          * @param {String} title The title bar text
2623          * @param {String} msg The message box body text
2624          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2625          * @param {Object} scope (optional) The scope of the callback function
2626          * @return {Roo.MessageBox} This message box
2627          */
2628         alert : function(title, msg, fn, scope){
2629             this.show({
2630                 title : title,
2631                 msg : msg,
2632                 buttons: this.OK,
2633                 fn: fn,
2634                 scope : scope,
2635                 modal : true
2636             });
2637             return this;
2638         },
2639
2640         /**
2641          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
2642          * interaction while waiting for a long-running process to complete that does not have defined intervals.
2643          * You are responsible for closing the message box when the process is complete.
2644          * @param {String} msg The message box body text
2645          * @param {String} title (optional) The title bar text
2646          * @return {Roo.MessageBox} This message box
2647          */
2648         wait : function(msg, title){
2649             this.show({
2650                 title : title,
2651                 msg : msg,
2652                 buttons: false,
2653                 closable:false,
2654                 progress:true,
2655                 modal:true,
2656                 width:300,
2657                 wait:true
2658             });
2659             waitTimer = Roo.TaskMgr.start({
2660                 run: function(i){
2661                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
2662                 },
2663                 interval: 1000
2664             });
2665             return this;
2666         },
2667
2668         /**
2669          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
2670          * If a callback function is passed it will be called after the user clicks either button, and the id of the
2671          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
2672          * @param {String} title The title bar text
2673          * @param {String} msg The message box body text
2674          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2675          * @param {Object} scope (optional) The scope of the callback function
2676          * @return {Roo.MessageBox} This message box
2677          */
2678         confirm : function(title, msg, fn, scope){
2679             this.show({
2680                 title : title,
2681                 msg : msg,
2682                 buttons: this.YESNO,
2683                 fn: fn,
2684                 scope : scope,
2685                 modal : true
2686             });
2687             return this;
2688         },
2689
2690         /**
2691          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
2692          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
2693          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
2694          * (could also be the top-right close button) and the text that was entered will be passed as the two
2695          * parameters to the callback.
2696          * @param {String} title The title bar text
2697          * @param {String} msg The message box body text
2698          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2699          * @param {Object} scope (optional) The scope of the callback function
2700          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
2701          * property, or the height in pixels to create the textbox (defaults to false / single-line)
2702          * @return {Roo.MessageBox} This message box
2703          */
2704         prompt : function(title, msg, fn, scope, multiline){
2705             this.show({
2706                 title : title,
2707                 msg : msg,
2708                 buttons: this.OKCANCEL,
2709                 fn: fn,
2710                 minWidth:250,
2711                 scope : scope,
2712                 prompt:true,
2713                 multiline: multiline,
2714                 modal : true
2715             });
2716             return this;
2717         },
2718
2719         /**
2720          * Button config that displays a single OK button
2721          * @type Object
2722          */
2723         OK : {ok:true},
2724         /**
2725          * Button config that displays Yes and No buttons
2726          * @type Object
2727          */
2728         YESNO : {yes:true, no:true},
2729         /**
2730          * Button config that displays OK and Cancel buttons
2731          * @type Object
2732          */
2733         OKCANCEL : {ok:true, cancel:true},
2734         /**
2735          * Button config that displays Yes, No and Cancel buttons
2736          * @type Object
2737          */
2738         YESNOCANCEL : {yes:true, no:true, cancel:true},
2739
2740         /**
2741          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
2742          * @type Number
2743          */
2744         defaultTextHeight : 75,
2745         /**
2746          * The maximum width in pixels of the message box (defaults to 600)
2747          * @type Number
2748          */
2749         maxWidth : 600,
2750         /**
2751          * The minimum width in pixels of the message box (defaults to 100)
2752          * @type Number
2753          */
2754         minWidth : 100,
2755         /**
2756          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
2757          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
2758          * @type Number
2759          */
2760         minProgressWidth : 250,
2761         /**
2762          * An object containing the default button text strings that can be overriden for localized language support.
2763          * Supported properties are: ok, cancel, yes and no.
2764          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
2765          * @type Object
2766          */
2767         buttonText : {
2768             ok : "OK",
2769             cancel : "Cancel",
2770             yes : "Yes",
2771             no : "No"
2772         }
2773     };
2774 }();
2775
2776 /**
2777  * Shorthand for {@link Roo.MessageBox}
2778  */
2779 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox 
2780 Roo.Msg = Roo.Msg || Roo.MessageBox;
2781 /*
2782  * - LGPL
2783  *
2784  * navbar
2785  * 
2786  */
2787
2788 /**
2789  * @class Roo.bootstrap.Navbar
2790  * @extends Roo.bootstrap.Component
2791  * Bootstrap Navbar class
2792
2793  * @constructor
2794  * Create a new Navbar
2795  * @param {Object} config The config object
2796  */
2797
2798
2799 Roo.bootstrap.Navbar = function(config){
2800     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
2801     
2802 };
2803
2804 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
2805     
2806     
2807    
2808     // private
2809     navItems : false,
2810     loadMask : false,
2811     
2812     
2813     getAutoCreate : function(){
2814         
2815         
2816         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
2817         
2818     },
2819     
2820     initEvents :function ()
2821     {
2822         //Roo.log(this.el.select('.navbar-toggle',true));
2823         this.el.select('.navbar-toggle',true).on('click', function() {
2824            // Roo.log('click');
2825             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
2826         }, this);
2827         
2828         var mark = {
2829             tag: "div",
2830             cls:"x-dlg-mask"
2831         }
2832         
2833         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
2834         
2835         var size = this.el.getSize();
2836         this.maskEl.setSize(size.width, size.height);
2837         this.maskEl.enableDisplayMode("block");
2838         this.maskEl.hide();
2839         
2840         if(this.loadMask){
2841             this.maskEl.show();
2842         }
2843     },
2844     
2845     
2846     getChildContainer : function()
2847     {
2848         if (this.el.select('.collapse').getCount()) {
2849             return this.el.select('.collapse',true).first();
2850         }
2851         
2852         return this.el;
2853     },
2854     
2855     mask : function()
2856     {
2857         this.maskEl.show();
2858     },
2859     
2860     unmask : function()
2861     {
2862         this.maskEl.hide();
2863     } 
2864     
2865     
2866     
2867     
2868 });
2869
2870
2871
2872  
2873
2874  /*
2875  * - LGPL
2876  *
2877  * navbar
2878  * 
2879  */
2880
2881 /**
2882  * @class Roo.bootstrap.NavSimplebar
2883  * @extends Roo.bootstrap.Navbar
2884  * Bootstrap Sidebar class
2885  *
2886  * @cfg {Boolean} inverse is inverted color
2887  * 
2888  * @cfg {String} type (nav | pills | tabs)
2889  * @cfg {Boolean} arrangement stacked | justified
2890  * @cfg {String} align (left | right) alignment
2891  * 
2892  * @cfg {Boolean} main (true|false) main nav bar? default false
2893  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
2894  * 
2895  * @cfg {String} tag (header|footer|nav|div) default is nav 
2896
2897  * 
2898  * 
2899  * 
2900  * @constructor
2901  * Create a new Sidebar
2902  * @param {Object} config The config object
2903  */
2904
2905
2906 Roo.bootstrap.NavSimplebar = function(config){
2907     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
2908 };
2909
2910 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
2911     
2912     inverse: false,
2913     
2914     type: false,
2915     arrangement: '',
2916     align : false,
2917     
2918     
2919     
2920     main : false,
2921     
2922     
2923     tag : false,
2924     
2925     
2926     getAutoCreate : function(){
2927         
2928         
2929         var cfg = {
2930             tag : this.tag || 'div',
2931             cls : 'navbar'
2932         };
2933           
2934         
2935         cfg.cn = [
2936             {
2937                 cls: 'nav',
2938                 tag : 'ul'
2939             }
2940         ];
2941         
2942          
2943         this.type = this.type || 'nav';
2944         if (['tabs','pills'].indexOf(this.type)!==-1) {
2945             cfg.cn[0].cls += ' nav-' + this.type
2946         
2947         
2948         } else {
2949             if (this.type!=='nav') {
2950                 Roo.log('nav type must be nav/tabs/pills')
2951             }
2952             cfg.cn[0].cls += ' navbar-nav'
2953         }
2954         
2955         
2956         
2957         
2958         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
2959             cfg.cn[0].cls += ' nav-' + this.arrangement;
2960         }
2961         
2962         
2963         if (this.align === 'right') {
2964             cfg.cn[0].cls += ' navbar-right';
2965         }
2966         
2967         if (this.inverse) {
2968             cfg.cls += ' navbar-inverse';
2969             
2970         }
2971         
2972         
2973         return cfg;
2974     
2975         
2976     }
2977     
2978     
2979     
2980 });
2981
2982
2983
2984  
2985
2986  
2987        /*
2988  * - LGPL
2989  *
2990  * navbar
2991  * 
2992  */
2993
2994 /**
2995  * @class Roo.bootstrap.NavHeaderbar
2996  * @extends Roo.bootstrap.NavSimplebar
2997  * Bootstrap Sidebar class
2998  *
2999  * @cfg {String} brand what is brand
3000  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3001  * @cfg {String} brand_href href of the brand
3002  * @cfg {Boolean} srButton generate the sr-only button (true | false) default true
3003  * 
3004  * @constructor
3005  * Create a new Sidebar
3006  * @param {Object} config The config object
3007  */
3008
3009
3010 Roo.bootstrap.NavHeaderbar = function(config){
3011     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3012 };
3013
3014 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3015     
3016     position: '',
3017     brand: '',
3018     brand_href: false,
3019     srButton : true,
3020     
3021     
3022     getAutoCreate : function(){
3023         
3024         var   cfg = {
3025             tag: this.nav || 'nav',
3026             cls: 'navbar',
3027             role: 'navigation',
3028             cn: []
3029         };
3030         
3031         if(this.srButton){
3032             cfg.cn.push({
3033                 tag: 'div',
3034                 cls: 'navbar-header',
3035                 cn: [
3036                     {
3037                         tag: 'button',
3038                         type: 'button',
3039                         cls: 'navbar-toggle',
3040                         'data-toggle': 'collapse',
3041                         cn: [
3042                             {
3043                                 tag: 'span',
3044                                 cls: 'sr-only',
3045                                 html: 'Toggle navigation'
3046                             },
3047                             {
3048                                 tag: 'span',
3049                                 cls: 'icon-bar'
3050                             },
3051                             {
3052                                 tag: 'span',
3053                                 cls: 'icon-bar'
3054                             },
3055                             {
3056                                 tag: 'span',
3057                                 cls: 'icon-bar'
3058                             }
3059                         ]
3060                     }
3061                 ]
3062             });
3063         }
3064         
3065         cfg.cn.push({
3066             tag: 'div',
3067             cls: 'collapse navbar-collapse',
3068             cn : []
3069         });
3070         
3071         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3072         
3073         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3074             cfg.cls += ' navbar-' + this.position;
3075             
3076             // tag can override this..
3077             
3078             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3079         }
3080         
3081         if (this.brand !== '') {
3082             cfg.cn[0].cn.push({
3083                 tag: 'a',
3084                 href: this.brand_href ? this.brand_href : '#',
3085                 cls: 'navbar-brand',
3086                 cn: [
3087                 this.brand
3088                 ]
3089             });
3090         }
3091         
3092         if(this.main){
3093             cfg.cls += ' main-nav';
3094         }
3095         
3096         
3097         return cfg;
3098
3099         
3100     }
3101     
3102     
3103     
3104 });
3105
3106
3107
3108  
3109
3110  /*
3111  * - LGPL
3112  *
3113  * navbar
3114  * 
3115  */
3116
3117 /**
3118  * @class Roo.bootstrap.NavSidebar
3119  * @extends Roo.bootstrap.Navbar
3120  * Bootstrap Sidebar class
3121  * 
3122  * @constructor
3123  * Create a new Sidebar
3124  * @param {Object} config The config object
3125  */
3126
3127
3128 Roo.bootstrap.NavSidebar = function(config){
3129     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3130 };
3131
3132 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3133     
3134     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3135     
3136     getAutoCreate : function(){
3137         
3138         
3139         return  {
3140             tag: 'div',
3141             cls: 'sidebar sidebar-nav'
3142         };
3143     
3144         
3145     }
3146     
3147     
3148     
3149 });
3150
3151
3152
3153  
3154
3155  /*
3156  * - LGPL
3157  *
3158  * nav group
3159  * 
3160  */
3161
3162 /**
3163  * @class Roo.bootstrap.NavGroup
3164  * @extends Roo.bootstrap.Component
3165  * Bootstrap NavGroup class
3166  * @cfg {String} align left | right
3167  * @cfg {Boolean} inverse false | true
3168  * @cfg {String} type (nav|pills|tab) default nav
3169  * @cfg {String} navId - reference Id for navbar.
3170
3171  * 
3172  * @constructor
3173  * Create a new nav group
3174  * @param {Object} config The config object
3175  */
3176
3177 Roo.bootstrap.NavGroup = function(config){
3178     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3179     this.navItems = [];
3180    
3181     Roo.bootstrap.NavGroup.register(this);
3182      this.addEvents({
3183         /**
3184              * @event changed
3185              * Fires when the active item changes
3186              * @param {Roo.bootstrap.NavGroup} this
3187              * @param {Roo.bootstrap.Navbar.Item} item The item selected
3188              * @param {Roo.bootstrap.Navbar.Item} item The previously selected item 
3189          */
3190         'changed': true
3191      });
3192     
3193 };
3194
3195 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3196     
3197     align: '',
3198     inverse: false,
3199     form: false,
3200     type: 'nav',
3201     navId : '',
3202     // private
3203     
3204     navItems : false, 
3205     
3206     getAutoCreate : function()
3207     {
3208         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3209         
3210         cfg = {
3211             tag : 'ul',
3212             cls: 'nav' 
3213         }
3214         
3215         if (['tabs','pills'].indexOf(this.type)!==-1) {
3216             cfg.cls += ' nav-' + this.type
3217         } else {
3218             if (this.type!=='nav') {
3219                 Roo.log('nav type must be nav/tabs/pills')
3220             }
3221             cfg.cls += ' navbar-nav'
3222         }
3223         
3224         if (this.parent().sidebar) {
3225             cfg = {
3226                 tag: 'ul',
3227                 cls: 'dashboard-menu sidebar-menu'
3228             }
3229             
3230             return cfg;
3231         }
3232         
3233         if (this.form === true) {
3234             cfg = {
3235                 tag: 'form',
3236                 cls: 'navbar-form'
3237             }
3238             
3239             if (this.align === 'right') {
3240                 cfg.cls += ' navbar-right';
3241             } else {
3242                 cfg.cls += ' navbar-left';
3243             }
3244         }
3245         
3246         if (this.align === 'right') {
3247             cfg.cls += ' navbar-right';
3248         }
3249         
3250         if (this.inverse) {
3251             cfg.cls += ' navbar-inverse';
3252             
3253         }
3254         
3255         
3256         return cfg;
3257     },
3258     /**
3259     * sets the active Navigation item
3260     * @param {Roo.bootstrap.NavItem} the new current navitem
3261     */
3262     setActiveItem : function(item)
3263     {
3264         var prev = false;
3265         Roo.each(this.navItems, function(v){
3266             if (v == item) {
3267                 return ;
3268             }
3269             if (v.isActive()) {
3270                 v.setActive(false, true);
3271                 prev = v;
3272                 
3273             }
3274             
3275         });
3276
3277         item.setActive(true, true);
3278         this.fireEvent('changed', this, item, prev);
3279         
3280         
3281     },
3282     /**
3283     * gets the active Navigation item
3284     * @return {Roo.bootstrap.NavItem} the current navitem
3285     */
3286     getActive : function()
3287     {
3288         
3289         var prev = false;
3290         Roo.each(this.navItems, function(v){
3291             
3292             if (v.isActive()) {
3293                 prev = v;
3294                 
3295             }
3296             
3297         });
3298         return prev;
3299     },
3300     /**
3301     * adds a Navigation item
3302     * @param {Roo.bootstrap.NavItem} the navitem to add
3303     */
3304     addItem : function(cfg)
3305     {
3306         var cn = new Roo.bootstrap.NavItem(cfg);
3307         this.register(cn);
3308         cn.parentId = this.id;
3309         cn.onRender(this.el, null);
3310         return cn;
3311     },
3312     /**
3313     * register a Navigation item
3314     * @param {Roo.bootstrap.NavItem} the navitem to add
3315     */
3316     register : function(item)
3317     {
3318         this.navItems.push( item);
3319         item.navId = this.navId;
3320     
3321     },
3322   
3323     
3324     getNavItem: function(tabId)
3325     {
3326         var ret = false;
3327         Roo.each(this.navItems, function(e) {
3328             if (e.tabId == tabId) {
3329                ret =  e;
3330                return false;
3331             }
3332             return true;
3333             
3334         });
3335         return ret;
3336     }
3337     
3338     
3339     
3340     
3341 });
3342
3343  
3344 Roo.apply(Roo.bootstrap.NavGroup, {
3345     
3346     groups: {},
3347      /**
3348     * register a Navigation Group
3349     * @param {Roo.bootstrap.NavGroup} the navgroup to add
3350     */
3351     register : function(navgrp)
3352     {
3353         this.groups[navgrp.navId] = navgrp;
3354         
3355     },
3356     /**
3357     * fetch a Navigation Group based on the navigation ID
3358     * @param {string} the navgroup to add
3359     * @returns {Roo.bootstrap.NavGroup} the navgroup 
3360     */
3361     get: function(navId) {
3362         if (typeof(this.groups[navId]) == 'undefined') {
3363             return false;
3364             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
3365         }
3366         return this.groups[navId] ;
3367     }
3368     
3369     
3370     
3371 });
3372
3373  /*
3374  * - LGPL
3375  *
3376  * row
3377  * 
3378  */
3379
3380 /**
3381  * @class Roo.bootstrap.NavItem
3382  * @extends Roo.bootstrap.Component
3383  * Bootstrap Navbar.NavItem class
3384  * @cfg {String} href  link to
3385  * @cfg {String} html content of button
3386  * @cfg {String} badge text inside badge
3387  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
3388  * @cfg {String} glyphicon name of glyphicon
3389  * @cfg {String} icon name of font awesome icon
3390  * @cfg {Boolean} active Is item active
3391  * @cfg {Boolean} disabled Is item disabled
3392  
3393  * @cfg {Boolean} preventDefault (true | false) default false
3394  * @cfg {String} tabId the tab that this item activates.
3395  * @cfg {String} tagtype (a|span) render as a href or span?
3396   
3397  * @constructor
3398  * Create a new Navbar Item
3399  * @param {Object} config The config object
3400  */
3401 Roo.bootstrap.NavItem = function(config){
3402     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
3403     this.addEvents({
3404         // raw events
3405         /**
3406          * @event click
3407          * The raw click event for the entire grid.
3408          * @param {Roo.EventObject} e
3409          */
3410         "click" : true,
3411          /**
3412             * @event changed
3413             * Fires when the active item active state changes
3414             * @param {Roo.bootstrap.NavItem} this
3415             * @param {boolean} state the new state
3416              
3417          */
3418         'changed': true
3419     });
3420    
3421 };
3422
3423 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
3424     
3425     href: false,
3426     html: '',
3427     badge: '',
3428     icon: false,
3429     glyphicon: false,
3430     active: false,
3431     preventDefault : false,
3432     tabId : false,
3433     tagtype : 'a',
3434     disabled : false,
3435     
3436     getAutoCreate : function(){
3437          
3438         var cfg = {
3439             tag: 'li',
3440             cls: 'nav-item'
3441             
3442         }
3443         if (this.active) {
3444             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
3445         }
3446         if (this.disabled) {
3447             cfg.cls += ' disabled';
3448         }
3449         
3450         if (this.href || this.html || this.glyphicon || this.icon) {
3451             cfg.cn = [
3452                 {
3453                     tag: this.tagtype,
3454                     href : this.href || "#",
3455                     html: this.html || ''
3456                 }
3457             ];
3458             
3459             if (this.icon) {
3460                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
3461             }
3462
3463             if(this.glyphicon) {
3464                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
3465             }
3466             
3467             if (this.menu) {
3468                 
3469                 cfg.cn[0].html += " <span class='caret'></span>";
3470              
3471             }
3472             
3473             if (this.badge !== '') {
3474                  
3475                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
3476             }
3477         }
3478         
3479         
3480         
3481         return cfg;
3482     },
3483     initEvents: function() {
3484        // Roo.log('init events?');
3485        // Roo.log(this.el.dom);
3486         if (typeof (this.menu) != 'undefined') {
3487             this.menu.parentType = this.xtype;
3488             this.menu.triggerEl = this.el;
3489             this.addxtype(Roo.apply({}, this.menu));
3490         }
3491
3492        
3493         this.el.select('a',true).on('click', this.onClick, this);
3494         // at this point parent should be available..
3495         this.parent().register(this);
3496     },
3497     
3498     onClick : function(e)
3499     {
3500          
3501         if(this.preventDefault){
3502             e.preventDefault();
3503         }
3504         if (this.disabled) {
3505             return;
3506         }
3507         Roo.log("fire event clicked");
3508         if(this.fireEvent('click', this, e) === false){
3509             return;
3510         };
3511         
3512         if (['tabs','pills'].indexOf(this.parent().type)!==-1) {
3513             if (typeof(this.parent().setActiveItem) !== 'undefined') {
3514                 this.parent().setActiveItem(this);
3515             }
3516             
3517             
3518             
3519         } 
3520     },
3521     
3522     isActive: function () {
3523         return this.active
3524     },
3525     setActive : function(state, fire)
3526     {
3527         this.active = state;
3528         if (!state ) {
3529             this.el.removeClass('active');
3530         } else if (!this.el.hasClass('active')) {
3531             this.el.addClass('active');
3532         }
3533         if (fire) {
3534             this.fireEvent('changed', this, state);
3535         }
3536         
3537         
3538     },
3539      // this should not be here...
3540     setDisabled : function(state)
3541     {
3542         this.disabled = state;
3543         if (!state ) {
3544             this.el.removeClass('disabled');
3545         } else if (!this.el.hasClass('disabled')) {
3546             this.el.addClass('disabled');
3547         }
3548         
3549     }
3550 });
3551  
3552
3553  /*
3554  * - LGPL
3555  *
3556  * sidebar item
3557  *
3558  *  li
3559  *    <span> icon </span>
3560  *    <span> text </span>
3561  *    <span>badge </span>
3562  */
3563
3564 /**
3565  * @class Roo.bootstrap.NavSidebarItem
3566  * @extends Roo.bootstrap.NavItem
3567  * Bootstrap Navbar.NavSidebarItem class
3568  * @constructor
3569  * Create a new Navbar Button
3570  * @param {Object} config The config object
3571  */
3572 Roo.bootstrap.NavSidebarItem = function(config){
3573     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
3574     this.addEvents({
3575         // raw events
3576         /**
3577          * @event click
3578          * The raw click event for the entire grid.
3579          * @param {Roo.EventObject} e
3580          */
3581         "click" : true,
3582          /**
3583             * @event changed
3584             * Fires when the active item active state changes
3585             * @param {Roo.bootstrap.NavSidebarItem} this
3586             * @param {boolean} state the new state
3587              
3588          */
3589         'changed': true
3590     });
3591    
3592 };
3593
3594 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
3595     
3596     
3597     getAutoCreate : function(){
3598         
3599         
3600         var a = {
3601                 tag: 'a',
3602                 href : this.href || '#',
3603                 cls: '',
3604                 html : '',
3605                 cn : []
3606         };
3607         var cfg = {
3608             tag: 'li',
3609             cls: '',
3610             cn: [ a ]
3611         }
3612         var span = {
3613             tag: 'span',
3614             html : this.html || ''
3615         }
3616         
3617         
3618         if (this.active) {
3619             cfg.cls += ' active';
3620         }
3621         
3622         // left icon..
3623         if (this.glyphicon || this.icon) {
3624             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
3625             a.cn.push({ tag : 'i', cls : c }) ;
3626         }
3627         // html..
3628         a.cn.push(span);
3629         // then badge..
3630         if (this.badge !== '') {
3631             a.cn.push({ tag: 'span',  cls : 'badge pull-right ' + (this.badgecls || ''), html: this.badge }); 
3632         }
3633         // fi
3634         if (this.menu) {
3635             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
3636             a.cls += 'dropdown-toggle treeview' ;
3637             
3638         }
3639         
3640         
3641         
3642         return cfg;
3643          
3644            
3645     }
3646    
3647      
3648  
3649 });
3650  
3651
3652  /*
3653  * - LGPL
3654  *
3655  * row
3656  * 
3657  */
3658
3659 /**
3660  * @class Roo.bootstrap.Row
3661  * @extends Roo.bootstrap.Component
3662  * Bootstrap Row class (contains columns...)
3663  * 
3664  * @constructor
3665  * Create a new Row
3666  * @param {Object} config The config object
3667  */
3668
3669 Roo.bootstrap.Row = function(config){
3670     Roo.bootstrap.Row.superclass.constructor.call(this, config);
3671 };
3672
3673 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
3674     
3675     getAutoCreate : function(){
3676        return {
3677             cls: 'row clearfix'
3678        };
3679     }
3680     
3681     
3682 });
3683
3684  
3685
3686  /*
3687  * - LGPL
3688  *
3689  * element
3690  * 
3691  */
3692
3693 /**
3694  * @class Roo.bootstrap.Element
3695  * @extends Roo.bootstrap.Component
3696  * Bootstrap Element class
3697  * @cfg {String} html contents of the element
3698  * @cfg {String} tag tag of the element
3699  * @cfg {String} cls class of the element
3700  * 
3701  * @constructor
3702  * Create a new Element
3703  * @param {Object} config The config object
3704  */
3705
3706 Roo.bootstrap.Element = function(config){
3707     Roo.bootstrap.Element.superclass.constructor.call(this, config);
3708 };
3709
3710 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
3711     
3712     tag: 'div',
3713     cls: '',
3714     html: '',
3715      
3716     
3717     getAutoCreate : function(){
3718         
3719         var cfg = {
3720             tag: this.tag,
3721             cls: this.cls,
3722             html: this.html
3723         }
3724         
3725         
3726         
3727         return cfg;
3728     }
3729    
3730 });
3731
3732  
3733
3734  /*
3735  * - LGPL
3736  *
3737  * pagination
3738  * 
3739  */
3740
3741 /**
3742  * @class Roo.bootstrap.Pagination
3743  * @extends Roo.bootstrap.Component
3744  * Bootstrap Pagination class
3745  * @cfg {String} size xs | sm | md | lg
3746  * @cfg {Boolean} inverse false | true
3747  * 
3748  * @constructor
3749  * Create a new Pagination
3750  * @param {Object} config The config object
3751  */
3752
3753 Roo.bootstrap.Pagination = function(config){
3754     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
3755 };
3756
3757 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
3758     
3759     cls: false,
3760     size: false,
3761     inverse: false,
3762     
3763     getAutoCreate : function(){
3764         var cfg = {
3765             tag: 'ul',
3766                 cls: 'pagination'
3767         };
3768         if (this.inverse) {
3769             cfg.cls += ' inverse';
3770         }
3771         if (this.html) {
3772             cfg.html=this.html;
3773         }
3774         if (this.cls) {
3775             cfg.cls += " " + this.cls;
3776         }
3777         return cfg;
3778     }
3779    
3780 });
3781
3782  
3783
3784  /*
3785  * - LGPL
3786  *
3787  * Pagination item
3788  * 
3789  */
3790
3791
3792 /**
3793  * @class Roo.bootstrap.PaginationItem
3794  * @extends Roo.bootstrap.Component
3795  * Bootstrap PaginationItem class
3796  * @cfg {String} html text
3797  * @cfg {String} href the link
3798  * @cfg {Boolean} preventDefault (true | false) default true
3799  * @cfg {Boolean} active (true | false) default false
3800  * 
3801  * 
3802  * @constructor
3803  * Create a new PaginationItem
3804  * @param {Object} config The config object
3805  */
3806
3807
3808 Roo.bootstrap.PaginationItem = function(config){
3809     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
3810     this.addEvents({
3811         // raw events
3812         /**
3813          * @event click
3814          * The raw click event for the entire grid.
3815          * @param {Roo.EventObject} e
3816          */
3817         "click" : true
3818     });
3819 };
3820
3821 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
3822     
3823     href : false,
3824     html : false,
3825     preventDefault: true,
3826     active : false,
3827     cls : false,
3828     
3829     getAutoCreate : function(){
3830         var cfg= {
3831             tag: 'li',
3832             cn: [
3833                 {
3834                     tag : 'a',
3835                     href : this.href ? this.href : '#',
3836                     html : this.html ? this.html : ''
3837                 }
3838             ]
3839         };
3840         
3841         if(this.cls){
3842             cfg.cls = this.cls;
3843         }
3844         
3845         if(this.active){
3846             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
3847         }
3848         
3849         return cfg;
3850     },
3851     
3852     initEvents: function() {
3853         
3854         this.el.on('click', this.onClick, this);
3855         
3856     },
3857     onClick : function(e)
3858     {
3859         Roo.log('PaginationItem on click ');
3860         if(this.preventDefault){
3861             e.preventDefault();
3862         }
3863         
3864         this.fireEvent('click', this, e);
3865     }
3866    
3867 });
3868
3869  
3870
3871  /*
3872  * - LGPL
3873  *
3874  * slider
3875  * 
3876  */
3877
3878
3879 /**
3880  * @class Roo.bootstrap.Slider
3881  * @extends Roo.bootstrap.Component
3882  * Bootstrap Slider class
3883  *    
3884  * @constructor
3885  * Create a new Slider
3886  * @param {Object} config The config object
3887  */
3888
3889 Roo.bootstrap.Slider = function(config){
3890     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
3891 };
3892
3893 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
3894     
3895     getAutoCreate : function(){
3896         
3897         var cfg = {
3898             tag: 'div',
3899             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
3900             cn: [
3901                 {
3902                     tag: 'a',
3903                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
3904                 }
3905             ]
3906         }
3907         
3908         return cfg;
3909     }
3910    
3911 });
3912
3913  /*
3914  * Based on:
3915  * Ext JS Library 1.1.1
3916  * Copyright(c) 2006-2007, Ext JS, LLC.
3917  *
3918  * Originally Released Under LGPL - original licence link has changed is not relivant.
3919  *
3920  * Fork - LGPL
3921  * <script type="text/javascript">
3922  */
3923  
3924
3925 /**
3926  * @class Roo.grid.ColumnModel
3927  * @extends Roo.util.Observable
3928  * This is the default implementation of a ColumnModel used by the Grid. It defines
3929  * the columns in the grid.
3930  * <br>Usage:<br>
3931  <pre><code>
3932  var colModel = new Roo.grid.ColumnModel([
3933         {header: "Ticker", width: 60, sortable: true, locked: true},
3934         {header: "Company Name", width: 150, sortable: true},
3935         {header: "Market Cap.", width: 100, sortable: true},
3936         {header: "$ Sales", width: 100, sortable: true, renderer: money},
3937         {header: "Employees", width: 100, sortable: true, resizable: false}
3938  ]);
3939  </code></pre>
3940  * <p>
3941  
3942  * The config options listed for this class are options which may appear in each
3943  * individual column definition.
3944  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
3945  * @constructor
3946  * @param {Object} config An Array of column config objects. See this class's
3947  * config objects for details.
3948 */
3949 Roo.grid.ColumnModel = function(config){
3950         /**
3951      * The config passed into the constructor
3952      */
3953     this.config = config;
3954     this.lookup = {};
3955
3956     // if no id, create one
3957     // if the column does not have a dataIndex mapping,
3958     // map it to the order it is in the config
3959     for(var i = 0, len = config.length; i < len; i++){
3960         var c = config[i];
3961         if(typeof c.dataIndex == "undefined"){
3962             c.dataIndex = i;
3963         }
3964         if(typeof c.renderer == "string"){
3965             c.renderer = Roo.util.Format[c.renderer];
3966         }
3967         if(typeof c.id == "undefined"){
3968             c.id = Roo.id();
3969         }
3970         if(c.editor && c.editor.xtype){
3971             c.editor  = Roo.factory(c.editor, Roo.grid);
3972         }
3973         if(c.editor && c.editor.isFormField){
3974             c.editor = new Roo.grid.GridEditor(c.editor);
3975         }
3976         this.lookup[c.id] = c;
3977     }
3978
3979     /**
3980      * The width of columns which have no width specified (defaults to 100)
3981      * @type Number
3982      */
3983     this.defaultWidth = 100;
3984
3985     /**
3986      * Default sortable of columns which have no sortable specified (defaults to false)
3987      * @type Boolean
3988      */
3989     this.defaultSortable = false;
3990
3991     this.addEvents({
3992         /**
3993              * @event widthchange
3994              * Fires when the width of a column changes.
3995              * @param {ColumnModel} this
3996              * @param {Number} columnIndex The column index
3997              * @param {Number} newWidth The new width
3998              */
3999             "widthchange": true,
4000         /**
4001              * @event headerchange
4002              * Fires when the text of a header changes.
4003              * @param {ColumnModel} this
4004              * @param {Number} columnIndex The column index
4005              * @param {Number} newText The new header text
4006              */
4007             "headerchange": true,
4008         /**
4009              * @event hiddenchange
4010              * Fires when a column is hidden or "unhidden".
4011              * @param {ColumnModel} this
4012              * @param {Number} columnIndex The column index
4013              * @param {Boolean} hidden true if hidden, false otherwise
4014              */
4015             "hiddenchange": true,
4016             /**
4017          * @event columnmoved
4018          * Fires when a column is moved.
4019          * @param {ColumnModel} this
4020          * @param {Number} oldIndex
4021          * @param {Number} newIndex
4022          */
4023         "columnmoved" : true,
4024         /**
4025          * @event columlockchange
4026          * Fires when a column's locked state is changed
4027          * @param {ColumnModel} this
4028          * @param {Number} colIndex
4029          * @param {Boolean} locked true if locked
4030          */
4031         "columnlockchange" : true
4032     });
4033     Roo.grid.ColumnModel.superclass.constructor.call(this);
4034 };
4035 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4036     /**
4037      * @cfg {String} header The header text to display in the Grid view.
4038      */
4039     /**
4040      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4041      * {@link Roo.data.Record} definition from which to draw the column's value. If not
4042      * specified, the column's index is used as an index into the Record's data Array.
4043      */
4044     /**
4045      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4046      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4047      */
4048     /**
4049      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4050      * Defaults to the value of the {@link #defaultSortable} property.
4051      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4052      */
4053     /**
4054      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
4055      */
4056     /**
4057      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
4058      */
4059     /**
4060      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4061      */
4062     /**
4063      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4064      */
4065     /**
4066      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4067      * given the cell's data value. See {@link #setRenderer}. If not specified, the
4068      * default renderer uses the raw data value. If an object is returned (bootstrap only)
4069      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4070      */
4071        /**
4072      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
4073      */
4074     /**
4075      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
4076      */
4077
4078     /**
4079      * Returns the id of the column at the specified index.
4080      * @param {Number} index The column index
4081      * @return {String} the id
4082      */
4083     getColumnId : function(index){
4084         return this.config[index].id;
4085     },
4086
4087     /**
4088      * Returns the column for a specified id.
4089      * @param {String} id The column id
4090      * @return {Object} the column
4091      */
4092     getColumnById : function(id){
4093         return this.lookup[id];
4094     },
4095
4096     
4097     /**
4098      * Returns the column for a specified dataIndex.
4099      * @param {String} dataIndex The column dataIndex
4100      * @return {Object|Boolean} the column or false if not found
4101      */
4102     getColumnByDataIndex: function(dataIndex){
4103         var index = this.findColumnIndex(dataIndex);
4104         return index > -1 ? this.config[index] : false;
4105     },
4106     
4107     /**
4108      * Returns the index for a specified column id.
4109      * @param {String} id The column id
4110      * @return {Number} the index, or -1 if not found
4111      */
4112     getIndexById : function(id){
4113         for(var i = 0, len = this.config.length; i < len; i++){
4114             if(this.config[i].id == id){
4115                 return i;
4116             }
4117         }
4118         return -1;
4119     },
4120     
4121     /**
4122      * Returns the index for a specified column dataIndex.
4123      * @param {String} dataIndex The column dataIndex
4124      * @return {Number} the index, or -1 if not found
4125      */
4126     
4127     findColumnIndex : function(dataIndex){
4128         for(var i = 0, len = this.config.length; i < len; i++){
4129             if(this.config[i].dataIndex == dataIndex){
4130                 return i;
4131             }
4132         }
4133         return -1;
4134     },
4135     
4136     
4137     moveColumn : function(oldIndex, newIndex){
4138         var c = this.config[oldIndex];
4139         this.config.splice(oldIndex, 1);
4140         this.config.splice(newIndex, 0, c);
4141         this.dataMap = null;
4142         this.fireEvent("columnmoved", this, oldIndex, newIndex);
4143     },
4144
4145     isLocked : function(colIndex){
4146         return this.config[colIndex].locked === true;
4147     },
4148
4149     setLocked : function(colIndex, value, suppressEvent){
4150         if(this.isLocked(colIndex) == value){
4151             return;
4152         }
4153         this.config[colIndex].locked = value;
4154         if(!suppressEvent){
4155             this.fireEvent("columnlockchange", this, colIndex, value);
4156         }
4157     },
4158
4159     getTotalLockedWidth : function(){
4160         var totalWidth = 0;
4161         for(var i = 0; i < this.config.length; i++){
4162             if(this.isLocked(i) && !this.isHidden(i)){
4163                 this.totalWidth += this.getColumnWidth(i);
4164             }
4165         }
4166         return totalWidth;
4167     },
4168
4169     getLockedCount : function(){
4170         for(var i = 0, len = this.config.length; i < len; i++){
4171             if(!this.isLocked(i)){
4172                 return i;
4173             }
4174         }
4175     },
4176
4177     /**
4178      * Returns the number of columns.
4179      * @return {Number}
4180      */
4181     getColumnCount : function(visibleOnly){
4182         if(visibleOnly === true){
4183             var c = 0;
4184             for(var i = 0, len = this.config.length; i < len; i++){
4185                 if(!this.isHidden(i)){
4186                     c++;
4187                 }
4188             }
4189             return c;
4190         }
4191         return this.config.length;
4192     },
4193
4194     /**
4195      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
4196      * @param {Function} fn
4197      * @param {Object} scope (optional)
4198      * @return {Array} result
4199      */
4200     getColumnsBy : function(fn, scope){
4201         var r = [];
4202         for(var i = 0, len = this.config.length; i < len; i++){
4203             var c = this.config[i];
4204             if(fn.call(scope||this, c, i) === true){
4205                 r[r.length] = c;
4206             }
4207         }
4208         return r;
4209     },
4210
4211     /**
4212      * Returns true if the specified column is sortable.
4213      * @param {Number} col The column index
4214      * @return {Boolean}
4215      */
4216     isSortable : function(col){
4217         if(typeof this.config[col].sortable == "undefined"){
4218             return this.defaultSortable;
4219         }
4220         return this.config[col].sortable;
4221     },
4222
4223     /**
4224      * Returns the rendering (formatting) function defined for the column.
4225      * @param {Number} col The column index.
4226      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
4227      */
4228     getRenderer : function(col){
4229         if(!this.config[col].renderer){
4230             return Roo.grid.ColumnModel.defaultRenderer;
4231         }
4232         return this.config[col].renderer;
4233     },
4234
4235     /**
4236      * Sets the rendering (formatting) function for a column.
4237      * @param {Number} col The column index
4238      * @param {Function} fn The function to use to process the cell's raw data
4239      * to return HTML markup for the grid view. The render function is called with
4240      * the following parameters:<ul>
4241      * <li>Data value.</li>
4242      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
4243      * <li>css A CSS style string to apply to the table cell.</li>
4244      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
4245      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
4246      * <li>Row index</li>
4247      * <li>Column index</li>
4248      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
4249      */
4250     setRenderer : function(col, fn){
4251         this.config[col].renderer = fn;
4252     },
4253
4254     /**
4255      * Returns the width for the specified column.
4256      * @param {Number} col The column index
4257      * @return {Number}
4258      */
4259     getColumnWidth : function(col){
4260         return this.config[col].width * 1 || this.defaultWidth;
4261     },
4262
4263     /**
4264      * Sets the width for a column.
4265      * @param {Number} col The column index
4266      * @param {Number} width The new width
4267      */
4268     setColumnWidth : function(col, width, suppressEvent){
4269         this.config[col].width = width;
4270         this.totalWidth = null;
4271         if(!suppressEvent){
4272              this.fireEvent("widthchange", this, col, width);
4273         }
4274     },
4275
4276     /**
4277      * Returns the total width of all columns.
4278      * @param {Boolean} includeHidden True to include hidden column widths
4279      * @return {Number}
4280      */
4281     getTotalWidth : function(includeHidden){
4282         if(!this.totalWidth){
4283             this.totalWidth = 0;
4284             for(var i = 0, len = this.config.length; i < len; i++){
4285                 if(includeHidden || !this.isHidden(i)){
4286                     this.totalWidth += this.getColumnWidth(i);
4287                 }
4288             }
4289         }
4290         return this.totalWidth;
4291     },
4292
4293     /**
4294      * Returns the header for the specified column.
4295      * @param {Number} col The column index
4296      * @return {String}
4297      */
4298     getColumnHeader : function(col){
4299         return this.config[col].header;
4300     },
4301
4302     /**
4303      * Sets the header for a column.
4304      * @param {Number} col The column index
4305      * @param {String} header The new header
4306      */
4307     setColumnHeader : function(col, header){
4308         this.config[col].header = header;
4309         this.fireEvent("headerchange", this, col, header);
4310     },
4311
4312     /**
4313      * Returns the tooltip for the specified column.
4314      * @param {Number} col The column index
4315      * @return {String}
4316      */
4317     getColumnTooltip : function(col){
4318             return this.config[col].tooltip;
4319     },
4320     /**
4321      * Sets the tooltip for a column.
4322      * @param {Number} col The column index
4323      * @param {String} tooltip The new tooltip
4324      */
4325     setColumnTooltip : function(col, tooltip){
4326             this.config[col].tooltip = tooltip;
4327     },
4328
4329     /**
4330      * Returns the dataIndex for the specified column.
4331      * @param {Number} col The column index
4332      * @return {Number}
4333      */
4334     getDataIndex : function(col){
4335         return this.config[col].dataIndex;
4336     },
4337
4338     /**
4339      * Sets the dataIndex for a column.
4340      * @param {Number} col The column index
4341      * @param {Number} dataIndex The new dataIndex
4342      */
4343     setDataIndex : function(col, dataIndex){
4344         this.config[col].dataIndex = dataIndex;
4345     },
4346
4347     
4348     
4349     /**
4350      * Returns true if the cell is editable.
4351      * @param {Number} colIndex The column index
4352      * @param {Number} rowIndex The row index
4353      * @return {Boolean}
4354      */
4355     isCellEditable : function(colIndex, rowIndex){
4356         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
4357     },
4358
4359     /**
4360      * Returns the editor defined for the cell/column.
4361      * return false or null to disable editing.
4362      * @param {Number} colIndex The column index
4363      * @param {Number} rowIndex The row index
4364      * @return {Object}
4365      */
4366     getCellEditor : function(colIndex, rowIndex){
4367         return this.config[colIndex].editor;
4368     },
4369
4370     /**
4371      * Sets if a column is editable.
4372      * @param {Number} col The column index
4373      * @param {Boolean} editable True if the column is editable
4374      */
4375     setEditable : function(col, editable){
4376         this.config[col].editable = editable;
4377     },
4378
4379
4380     /**
4381      * Returns true if the column is hidden.
4382      * @param {Number} colIndex The column index
4383      * @return {Boolean}
4384      */
4385     isHidden : function(colIndex){
4386         return this.config[colIndex].hidden;
4387     },
4388
4389
4390     /**
4391      * Returns true if the column width cannot be changed
4392      */
4393     isFixed : function(colIndex){
4394         return this.config[colIndex].fixed;
4395     },
4396
4397     /**
4398      * Returns true if the column can be resized
4399      * @return {Boolean}
4400      */
4401     isResizable : function(colIndex){
4402         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
4403     },
4404     /**
4405      * Sets if a column is hidden.
4406      * @param {Number} colIndex The column index
4407      * @param {Boolean} hidden True if the column is hidden
4408      */
4409     setHidden : function(colIndex, hidden){
4410         this.config[colIndex].hidden = hidden;
4411         this.totalWidth = null;
4412         this.fireEvent("hiddenchange", this, colIndex, hidden);
4413     },
4414
4415     /**
4416      * Sets the editor for a column.
4417      * @param {Number} col The column index
4418      * @param {Object} editor The editor object
4419      */
4420     setEditor : function(col, editor){
4421         this.config[col].editor = editor;
4422     }
4423 });
4424
4425 Roo.grid.ColumnModel.defaultRenderer = function(value){
4426         if(typeof value == "string" && value.length < 1){
4427             return "&#160;";
4428         }
4429         return value;
4430 };
4431
4432 // Alias for backwards compatibility
4433 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
4434 /*
4435  * Based on:
4436  * Ext JS Library 1.1.1
4437  * Copyright(c) 2006-2007, Ext JS, LLC.
4438  *
4439  * Originally Released Under LGPL - original licence link has changed is not relivant.
4440  *
4441  * Fork - LGPL
4442  * <script type="text/javascript">
4443  */
4444  
4445 /**
4446  * @class Roo.LoadMask
4447  * A simple utility class for generically masking elements while loading data.  If the element being masked has
4448  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
4449  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
4450  * element's UpdateManager load indicator and will be destroyed after the initial load.
4451  * @constructor
4452  * Create a new LoadMask
4453  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
4454  * @param {Object} config The config object
4455  */
4456 Roo.LoadMask = function(el, config){
4457     this.el = Roo.get(el);
4458     Roo.apply(this, config);
4459     if(this.store){
4460         this.store.on('beforeload', this.onBeforeLoad, this);
4461         this.store.on('load', this.onLoad, this);
4462         this.store.on('loadexception', this.onLoadException, this);
4463         this.removeMask = false;
4464     }else{
4465         var um = this.el.getUpdateManager();
4466         um.showLoadIndicator = false; // disable the default indicator
4467         um.on('beforeupdate', this.onBeforeLoad, this);
4468         um.on('update', this.onLoad, this);
4469         um.on('failure', this.onLoad, this);
4470         this.removeMask = true;
4471     }
4472 };
4473
4474 Roo.LoadMask.prototype = {
4475     /**
4476      * @cfg {Boolean} removeMask
4477      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
4478      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
4479      */
4480     /**
4481      * @cfg {String} msg
4482      * The text to display in a centered loading message box (defaults to 'Loading...')
4483      */
4484     msg : 'Loading...',
4485     /**
4486      * @cfg {String} msgCls
4487      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
4488      */
4489     msgCls : 'x-mask-loading',
4490
4491     /**
4492      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
4493      * @type Boolean
4494      */
4495     disabled: false,
4496
4497     /**
4498      * Disables the mask to prevent it from being displayed
4499      */
4500     disable : function(){
4501        this.disabled = true;
4502     },
4503
4504     /**
4505      * Enables the mask so that it can be displayed
4506      */
4507     enable : function(){
4508         this.disabled = false;
4509     },
4510     
4511     onLoadException : function()
4512     {
4513         Roo.log(arguments);
4514         
4515         if (typeof(arguments[3]) != 'undefined') {
4516             Roo.MessageBox.alert("Error loading",arguments[3]);
4517         } 
4518         /*
4519         try {
4520             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
4521                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
4522             }   
4523         } catch(e) {
4524             
4525         }
4526         */
4527     
4528         
4529         
4530         this.el.unmask(this.removeMask);
4531     },
4532     // private
4533     onLoad : function()
4534     {
4535         this.el.unmask(this.removeMask);
4536     },
4537
4538     // private
4539     onBeforeLoad : function(){
4540         if(!this.disabled){
4541             this.el.mask(this.msg, this.msgCls);
4542         }
4543     },
4544
4545     // private
4546     destroy : function(){
4547         if(this.store){
4548             this.store.un('beforeload', this.onBeforeLoad, this);
4549             this.store.un('load', this.onLoad, this);
4550             this.store.un('loadexception', this.onLoadException, this);
4551         }else{
4552             var um = this.el.getUpdateManager();
4553             um.un('beforeupdate', this.onBeforeLoad, this);
4554             um.un('update', this.onLoad, this);
4555             um.un('failure', this.onLoad, this);
4556         }
4557     }
4558 };/*
4559  * - LGPL
4560  *
4561  * table
4562  * 
4563  */
4564
4565 /**
4566  * @class Roo.bootstrap.Table
4567  * @extends Roo.bootstrap.Component
4568  * Bootstrap Table class
4569  * @cfg {String} cls table class
4570  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
4571  * @cfg {String} bgcolor Specifies the background color for a table
4572  * @cfg {Number} border Specifies whether the table cells should have borders or not
4573  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
4574  * @cfg {Number} cellspacing Specifies the space between cells
4575  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
4576  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
4577  * @cfg {String} sortable Specifies that the table should be sortable
4578  * @cfg {String} summary Specifies a summary of the content of a table
4579  * @cfg {Number} width Specifies the width of a table
4580  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
4581  * 
4582  * @cfg {boolean} striped Should the rows be alternative striped
4583  * @cfg {boolean} bordered Add borders to the table
4584  * @cfg {boolean} hover Add hover highlighting
4585  * @cfg {boolean} condensed Format condensed
4586  * @cfg {boolean} responsive Format condensed
4587  * @cfg {Boolean} loadMask (true|false) default false
4588  * @cfg {Boolean} tfoot (true|false) generate tfoot, default true
4589  * @cfg {Boolean} thead (true|false) generate thead, default true
4590  * @cfg {Boolean} RowSelection (true|false) default false
4591  * @cfg {Boolean} CellSelection (true|false) default false
4592  *
4593  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
4594  
4595  * 
4596  * @constructor
4597  * Create a new Table
4598  * @param {Object} config The config object
4599  */
4600
4601 Roo.bootstrap.Table = function(config){
4602     Roo.bootstrap.Table.superclass.constructor.call(this, config);
4603     
4604     if (this.sm) {
4605         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
4606         this.sm = this.selModel;
4607         this.sm.xmodule = this.xmodule || false;
4608     }
4609     if (this.cm && typeof(this.cm.config) == 'undefined') {
4610         this.colModel = new Roo.grid.ColumnModel(this.cm);
4611         this.cm = this.colModel;
4612         this.cm.xmodule = this.xmodule || false;
4613     }
4614     if (this.store) {
4615         this.store= Roo.factory(this.store, Roo.data);
4616         this.ds = this.store;
4617         this.ds.xmodule = this.xmodule || false;
4618          
4619     }
4620     if (this.footer && this.store) {
4621         this.footer.dataSource = this.ds;
4622         this.footer = Roo.factory(this.footer);
4623     }
4624     
4625     /** @private */
4626     this.addEvents({
4627         /**
4628          * @event cellclick
4629          * Fires when a cell is clicked
4630          * @param {Roo.bootstrap.Table} this
4631          * @param {Roo.Element} el
4632          * @param {Number} rowIndex
4633          * @param {Number} columnIndex
4634          * @param {Roo.EventObject} e
4635          */
4636         "cellclick" : true,
4637         /**
4638          * @event celldblclick
4639          * Fires when a cell is double clicked
4640          * @param {Roo.bootstrap.Table} this
4641          * @param {Roo.Element} el
4642          * @param {Number} rowIndex
4643          * @param {Number} columnIndex
4644          * @param {Roo.EventObject} e
4645          */
4646         "celldblclick" : true,
4647         /**
4648          * @event rowclick
4649          * Fires when a row is clicked
4650          * @param {Roo.bootstrap.Table} this
4651          * @param {Roo.Element} el
4652          * @param {Number} rowIndex
4653          * @param {Roo.EventObject} e
4654          */
4655         "rowclick" : true,
4656         /**
4657          * @event rowdblclick
4658          * Fires when a row is double clicked
4659          * @param {Roo.bootstrap.Table} this
4660          * @param {Roo.Element} el
4661          * @param {Number} rowIndex
4662          * @param {Roo.EventObject} e
4663          */
4664         "rowdblclick" : true,
4665         /**
4666          * @event mouseover
4667          * Fires when a mouseover occur
4668          * @param {Roo.bootstrap.Table} this
4669          * @param {Roo.Element} el
4670          * @param {Number} rowIndex
4671          * @param {Number} columnIndex
4672          * @param {Roo.EventObject} e
4673          */
4674         "mouseover" : true,
4675         /**
4676          * @event mouseout
4677          * Fires when a mouseout occur
4678          * @param {Roo.bootstrap.Table} this
4679          * @param {Roo.Element} el
4680          * @param {Number} rowIndex
4681          * @param {Number} columnIndex
4682          * @param {Roo.EventObject} e
4683          */
4684         "mouseout" : true,
4685         /**
4686          * @event rowclass
4687          * Fires when a row is rendered, so you can change add a style to it.
4688          * @param {Roo.bootstrap.Table} this
4689          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
4690          */
4691         'rowclass' : true
4692         
4693     });
4694 };
4695
4696 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
4697     
4698     cls: false,
4699     align: false,
4700     bgcolor: false,
4701     border: false,
4702     cellpadding: false,
4703     cellspacing: false,
4704     frame: false,
4705     rules: false,
4706     sortable: false,
4707     summary: false,
4708     width: false,
4709     striped : false,
4710     bordered: false,
4711     hover:  false,
4712     condensed : false,
4713     responsive : false,
4714     sm : false,
4715     cm : false,
4716     store : false,
4717     loadMask : false,
4718     tfoot : true,
4719     thead : true,
4720     RowSelection : false,
4721     CellSelection : false,
4722     layout : false,
4723     
4724     // Roo.Element - the tbody
4725     mainBody: false, 
4726     
4727     getAutoCreate : function(){
4728         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
4729         
4730         cfg = {
4731             tag: 'table',
4732             cls : 'table',
4733             cn : []
4734         }
4735             
4736         if (this.striped) {
4737             cfg.cls += ' table-striped';
4738         }
4739         
4740         if (this.hover) {
4741             cfg.cls += ' table-hover';
4742         }
4743         if (this.bordered) {
4744             cfg.cls += ' table-bordered';
4745         }
4746         if (this.condensed) {
4747             cfg.cls += ' table-condensed';
4748         }
4749         if (this.responsive) {
4750             cfg.cls += ' table-responsive';
4751         }
4752         
4753         if (this.cls) {
4754             cfg.cls+=  ' ' +this.cls;
4755         }
4756         
4757         // this lot should be simplifed...
4758         
4759         if (this.align) {
4760             cfg.align=this.align;
4761         }
4762         if (this.bgcolor) {
4763             cfg.bgcolor=this.bgcolor;
4764         }
4765         if (this.border) {
4766             cfg.border=this.border;
4767         }
4768         if (this.cellpadding) {
4769             cfg.cellpadding=this.cellpadding;
4770         }
4771         if (this.cellspacing) {
4772             cfg.cellspacing=this.cellspacing;
4773         }
4774         if (this.frame) {
4775             cfg.frame=this.frame;
4776         }
4777         if (this.rules) {
4778             cfg.rules=this.rules;
4779         }
4780         if (this.sortable) {
4781             cfg.sortable=this.sortable;
4782         }
4783         if (this.summary) {
4784             cfg.summary=this.summary;
4785         }
4786         if (this.width) {
4787             cfg.width=this.width;
4788         }
4789         if (this.layout) {
4790             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
4791         }
4792         
4793         if(this.store || this.cm){
4794             if(this.thead){
4795                 cfg.cn.push(this.renderHeader());
4796             }
4797             
4798             cfg.cn.push(this.renderBody());
4799             
4800             if(this.tfoot){
4801                 cfg.cn.push(this.renderFooter());
4802             }
4803             
4804             cfg.cls+=  ' TableGrid';
4805         }
4806         
4807         return { cn : [ cfg ] };
4808     },
4809     
4810     initEvents : function()
4811     {   
4812         if(!this.store || !this.cm){
4813             return;
4814         }
4815         
4816         //Roo.log('initEvents with ds!!!!');
4817         
4818         this.mainBody = this.el.select('tbody', true).first();
4819         
4820         
4821         var _this = this;
4822         
4823         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
4824             e.on('click', _this.sort, _this);
4825         });
4826         
4827         this.el.on("click", this.onClick, this);
4828         this.el.on("dblclick", this.onDblClick, this);
4829         
4830         this.parent().el.setStyle('position', 'relative');
4831         if (this.footer) {
4832             this.footer.parentId = this.id;
4833             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
4834         }
4835         
4836         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
4837         
4838         this.store.on('load', this.onLoad, this);
4839         this.store.on('beforeload', this.onBeforeLoad, this);
4840         this.store.on('update', this.onUpdate, this);
4841         
4842     },
4843     
4844     onMouseover : function(e, el)
4845     {
4846         var cell = Roo.get(el);
4847         
4848         if(!cell){
4849             return;
4850         }
4851         
4852         if(e.getTarget().nodeName.toLowerCase() != 'td'){
4853             cell = cell.findParent('td', false, true);
4854         }
4855         
4856         var row = cell.findParent('tr', false, true);
4857         var cellIndex = cell.dom.cellIndex;
4858         var rowIndex = row.dom.rowIndex - 1; // start from 0
4859         
4860         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
4861         
4862     },
4863     
4864     onMouseout : function(e, el)
4865     {
4866         var cell = Roo.get(el);
4867         
4868         if(!cell){
4869             return;
4870         }
4871         
4872         if(e.getTarget().nodeName.toLowerCase() != 'td'){
4873             cell = cell.findParent('td', false, true);
4874         }
4875         
4876         var row = cell.findParent('tr', false, true);
4877         var cellIndex = cell.dom.cellIndex;
4878         var rowIndex = row.dom.rowIndex - 1; // start from 0
4879         
4880         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
4881         
4882     },
4883     
4884     onClick : function(e, el)
4885     {
4886         var cell = Roo.get(el);
4887         
4888         if(!cell || (!this.CellSelection && !this.RowSelection)){
4889             return;
4890         }
4891         
4892         
4893         if(e.getTarget().nodeName.toLowerCase() != 'td'){
4894             cell = cell.findParent('td', false, true);
4895         }
4896         
4897         var row = cell.findParent('tr', false, true);
4898         var cellIndex = cell.dom.cellIndex;
4899         var rowIndex = row.dom.rowIndex - 1;
4900         
4901         if(this.CellSelection){
4902             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
4903         }
4904         
4905         if(this.RowSelection){
4906             this.fireEvent('rowclick', this, row, rowIndex, e);
4907         }
4908         
4909         
4910     },
4911     
4912     onDblClick : function(e,el)
4913     {
4914         var cell = Roo.get(el);
4915         
4916         if(!cell || (!this.CellSelection && !this.RowSelection)){
4917             return;
4918         }
4919         
4920         if(e.getTarget().nodeName.toLowerCase() != 'td'){
4921             cell = cell.findParent('td', false, true);
4922         }
4923         
4924         var row = cell.findParent('tr', false, true);
4925         var cellIndex = cell.dom.cellIndex;
4926         var rowIndex = row.dom.rowIndex - 1;
4927         
4928         if(this.CellSelection){
4929             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
4930         }
4931         
4932         if(this.RowSelection){
4933             this.fireEvent('rowdblclick', this, row, rowIndex, e);
4934         }
4935     },
4936     
4937     sort : function(e,el)
4938     {
4939         var col = Roo.get(el)
4940         
4941         if(!col.hasClass('sortable')){
4942             return;
4943         }
4944         
4945         var sort = col.attr('sort');
4946         var dir = 'ASC';
4947         
4948         if(col.hasClass('glyphicon-arrow-up')){
4949             dir = 'DESC';
4950         }
4951         
4952         this.store.sortInfo = {field : sort, direction : dir};
4953         
4954         if (this.footer) {
4955             Roo.log("calling footer first");
4956             this.footer.onClick('first');
4957         } else {
4958         
4959             this.store.load({ params : { start : 0 } });
4960         }
4961     },
4962     
4963     renderHeader : function()
4964     {
4965         var header = {
4966             tag: 'thead',
4967             cn : []
4968         };
4969         
4970         var cm = this.cm;
4971         
4972         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
4973             
4974             var config = cm.config[i];
4975                     
4976             var c = {
4977                 tag: 'th',
4978                 style : '',
4979                 html: cm.getColumnHeader(i)
4980             };
4981             
4982             if(typeof(config.hidden) != 'undefined' && config.hidden){
4983                 c.style += ' display:none;';
4984             }
4985             
4986             if(typeof(config.dataIndex) != 'undefined'){
4987                 c.sort = config.dataIndex;
4988             }
4989             
4990             if(typeof(config.sortable) != 'undefined' && config.sortable){
4991                 c.cls = 'sortable';
4992             }
4993             
4994 //            if(typeof(config.align) != 'undefined' && config.align.length){
4995 //                c.style += ' text-align:' + config.align + ';';
4996 //            }
4997             
4998             if(typeof(config.width) != 'undefined'){
4999                 c.style += ' width:' + config.width + 'px;';
5000             }
5001             
5002             header.cn.push(c)
5003         }
5004         
5005         return header;
5006     },
5007     
5008     renderBody : function()
5009     {
5010         var body = {
5011             tag: 'tbody',
5012             cn : [
5013                 {
5014                     tag: 'tr',
5015                     cn : [
5016                         {
5017                             tag : 'td',
5018                             colspan :  this.cm.getColumnCount()
5019                         }
5020                     ]
5021                 }
5022             ]
5023         };
5024         
5025         return body;
5026     },
5027     
5028     renderFooter : function()
5029     {
5030         var footer = {
5031             tag: 'tfoot',
5032             cn : [
5033                 {
5034                     tag: 'tr',
5035                     cn : [
5036                         {
5037                             tag : 'td',
5038                             colspan :  this.cm.getColumnCount()
5039                         }
5040                     ]
5041                 }
5042             ]
5043         };
5044         
5045         return footer;
5046     },
5047     
5048     
5049     
5050     onLoad : function()
5051     {
5052         Roo.log('ds onload');
5053         this.clear();
5054         
5055         var _this = this;
5056         var cm = this.cm;
5057         var ds = this.store;
5058         
5059         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5060             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
5061             
5062             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
5063                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
5064             }
5065             
5066             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
5067                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
5068             }
5069         });
5070         
5071         var tbody =  this.mainBody;
5072         
5073         var renders = [];
5074                     
5075         if(ds.getCount() > 0){
5076             ds.data.each(function(d,rowIndex){
5077                 var row =  this.renderRow(cm, ds, rowIndex);
5078                 
5079                 tbody.createChild(row);
5080                 
5081                 var _this = this;
5082                 
5083                 if(row.cellObjects.length){
5084                     Roo.each(row.cellObjects, function(r){
5085                         _this.renderCellObject(r);
5086                     })
5087                 }
5088                 
5089             }, this);
5090         }
5091         
5092         Roo.each(this.el.select('tbody td', true).elements, function(e){
5093             e.on('mouseover', _this.onMouseover, _this);
5094         });
5095         
5096         Roo.each(this.el.select('tbody td', true).elements, function(e){
5097             e.on('mouseout', _this.onMouseout, _this);
5098         });
5099
5100         //if(this.loadMask){
5101         //    this.maskEl.hide();
5102         //}
5103     },
5104     
5105     
5106     onUpdate : function(ds,record)
5107     {
5108         this.refreshRow(record);
5109     },
5110     onRemove : function(ds, record, index, isUpdate){
5111         if(isUpdate !== true){
5112             this.fireEvent("beforerowremoved", this, index, record);
5113         }
5114         var bt = this.mainBody.dom;
5115         if(bt.rows[index]){
5116             bt.removeChild(bt.rows[index]);
5117         }
5118         
5119         if(isUpdate !== true){
5120             //this.stripeRows(index);
5121             //this.syncRowHeights(index, index);
5122             //this.layout();
5123             this.fireEvent("rowremoved", this, index, record);
5124         }
5125     },
5126     
5127     
5128     refreshRow : function(record){
5129         var ds = this.store, index;
5130         if(typeof record == 'number'){
5131             index = record;
5132             record = ds.getAt(index);
5133         }else{
5134             index = ds.indexOf(record);
5135         }
5136         this.insertRow(ds, index, true);
5137         this.onRemove(ds, record, index+1, true);
5138         //this.syncRowHeights(index, index);
5139         //this.layout();
5140         this.fireEvent("rowupdated", this, index, record);
5141     },
5142     
5143     insertRow : function(dm, rowIndex, isUpdate){
5144         
5145         if(!isUpdate){
5146             this.fireEvent("beforerowsinserted", this, rowIndex);
5147         }
5148             //var s = this.getScrollState();
5149         var row = this.renderRow(this.cm, this.store, rowIndex);
5150         // insert before rowIndex..
5151         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
5152         Roo.log(e);
5153         
5154         var _this = this;
5155                 
5156         if(row.cellObjects.length){
5157             Roo.each(row.cellObjects, function(r){
5158                 _this.renderCellObject(r);
5159             })
5160         }
5161             
5162         if(!isUpdate){
5163             this.fireEvent("rowsinserted", this, rowIndex);
5164             //this.syncRowHeights(firstRow, lastRow);
5165             //this.stripeRows(firstRow);
5166             //this.layout();
5167         }
5168         
5169     },
5170     
5171     
5172     getRowDom : function(rowIndex)
5173     {
5174         // not sure if I need to check this.. but let's do it anyway..
5175         return (this.mainBody.dom.rows && (rowIndex-1) < this.mainBody.dom.rows.length ) ?
5176                 this.mainBody.dom.rows[rowIndex] : false
5177     },
5178     // returns the object tree for a tr..
5179   
5180     
5181     renderRow : function(cm, ds, rowIndex) {
5182         
5183         var d = ds.getAt(rowIndex);
5184         
5185         var row = {
5186             tag : 'tr',
5187             cn : []
5188         };
5189             
5190         var cellObjects = [];
5191         
5192         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5193             var config = cm.config[i];
5194             
5195             var renderer = cm.getRenderer(i);
5196             var value = '';
5197             var id = false;
5198             
5199             if(typeof(renderer) !== 'undefined'){
5200                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
5201             }
5202             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
5203             // and are rendered into the cells after the row is rendered - using the id for the element.
5204             
5205             if(typeof(value) === 'object'){
5206                 id = Roo.id();
5207                 cellObjects.push({
5208                     container : id,
5209                     cfg : value 
5210                 })
5211             }
5212             
5213             var rowcfg = {
5214                 record: d,
5215                 rowIndex : rowIndex,
5216                 colIndex : i,
5217                 rowClass : ''
5218             }
5219
5220             this.fireEvent('rowclass', this, rowcfg);
5221             
5222             var td = {
5223                 tag: 'td',
5224                 cls : rowcfg.rowClass,
5225                 style: '',
5226                 html: (typeof(value) === 'object') ? '' : value
5227             };
5228             
5229             if (id) {
5230                 td.id = id;
5231             }
5232             
5233             if(typeof(config.hidden) != 'undefined' && config.hidden){
5234                 td.style += ' display:none;';
5235             }
5236             
5237             if(typeof(config.align) != 'undefined' && config.align.length){
5238                 td.style += ' text-align:' + config.align + ';';
5239             }
5240             
5241             if(typeof(config.width) != 'undefined'){
5242                 td.style += ' width:' +  config.width + 'px;';
5243             }
5244              
5245             row.cn.push(td);
5246            
5247         }
5248         
5249         row.cellObjects = cellObjects;
5250         
5251         return row;
5252           
5253     },
5254     
5255     
5256     
5257     onBeforeLoad : function()
5258     {
5259         //Roo.log('ds onBeforeLoad');
5260         
5261         //this.clear();
5262         
5263         //if(this.loadMask){
5264         //    this.maskEl.show();
5265         //}
5266     },
5267     
5268     clear : function()
5269     {
5270         this.el.select('tbody', true).first().dom.innerHTML = '';
5271     },
5272     
5273     getSelectionModel : function(){
5274         if(!this.selModel){
5275             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
5276         }
5277         return this.selModel;
5278     },
5279     /*
5280      * Render the Roo.bootstrap object from renderder
5281      */
5282     renderCellObject : function(r)
5283     {
5284         var _this = this;
5285         
5286         var t = r.cfg.render(r.container);
5287         
5288         if(r.cfg.cn){
5289             Roo.each(r.cfg.cn, function(c){
5290                 var child = {
5291                     container: t.getChildContainer(),
5292                     cfg: c
5293                 }
5294                 _this.renderCellObject(child);
5295             })
5296         }
5297     }
5298    
5299 });
5300
5301  
5302
5303  /*
5304  * - LGPL
5305  *
5306  * table cell
5307  * 
5308  */
5309
5310 /**
5311  * @class Roo.bootstrap.TableCell
5312  * @extends Roo.bootstrap.Component
5313  * Bootstrap TableCell class
5314  * @cfg {String} html cell contain text
5315  * @cfg {String} cls cell class
5316  * @cfg {String} tag cell tag (td|th) default td
5317  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
5318  * @cfg {String} align Aligns the content in a cell
5319  * @cfg {String} axis Categorizes cells
5320  * @cfg {String} bgcolor Specifies the background color of a cell
5321  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5322  * @cfg {Number} colspan Specifies the number of columns a cell should span
5323  * @cfg {String} headers Specifies one or more header cells a cell is related to
5324  * @cfg {Number} height Sets the height of a cell
5325  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
5326  * @cfg {Number} rowspan Sets the number of rows a cell should span
5327  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
5328  * @cfg {String} valign Vertical aligns the content in a cell
5329  * @cfg {Number} width Specifies the width of a cell
5330  * 
5331  * @constructor
5332  * Create a new TableCell
5333  * @param {Object} config The config object
5334  */
5335
5336 Roo.bootstrap.TableCell = function(config){
5337     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
5338 };
5339
5340 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
5341     
5342     html: false,
5343     cls: false,
5344     tag: false,
5345     abbr: false,
5346     align: false,
5347     axis: false,
5348     bgcolor: false,
5349     charoff: false,
5350     colspan: false,
5351     headers: false,
5352     height: false,
5353     nowrap: false,
5354     rowspan: false,
5355     scope: false,
5356     valign: false,
5357     width: false,
5358     
5359     
5360     getAutoCreate : function(){
5361         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
5362         
5363         cfg = {
5364             tag: 'td'
5365         }
5366         
5367         if(this.tag){
5368             cfg.tag = this.tag;
5369         }
5370         
5371         if (this.html) {
5372             cfg.html=this.html
5373         }
5374         if (this.cls) {
5375             cfg.cls=this.cls
5376         }
5377         if (this.abbr) {
5378             cfg.abbr=this.abbr
5379         }
5380         if (this.align) {
5381             cfg.align=this.align
5382         }
5383         if (this.axis) {
5384             cfg.axis=this.axis
5385         }
5386         if (this.bgcolor) {
5387             cfg.bgcolor=this.bgcolor
5388         }
5389         if (this.charoff) {
5390             cfg.charoff=this.charoff
5391         }
5392         if (this.colspan) {
5393             cfg.colspan=this.colspan
5394         }
5395         if (this.headers) {
5396             cfg.headers=this.headers
5397         }
5398         if (this.height) {
5399             cfg.height=this.height
5400         }
5401         if (this.nowrap) {
5402             cfg.nowrap=this.nowrap
5403         }
5404         if (this.rowspan) {
5405             cfg.rowspan=this.rowspan
5406         }
5407         if (this.scope) {
5408             cfg.scope=this.scope
5409         }
5410         if (this.valign) {
5411             cfg.valign=this.valign
5412         }
5413         if (this.width) {
5414             cfg.width=this.width
5415         }
5416         
5417         
5418         return cfg;
5419     }
5420    
5421 });
5422
5423  
5424
5425  /*
5426  * - LGPL
5427  *
5428  * table row
5429  * 
5430  */
5431
5432 /**
5433  * @class Roo.bootstrap.TableRow
5434  * @extends Roo.bootstrap.Component
5435  * Bootstrap TableRow class
5436  * @cfg {String} cls row class
5437  * @cfg {String} align Aligns the content in a table row
5438  * @cfg {String} bgcolor Specifies a background color for a table row
5439  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5440  * @cfg {String} valign Vertical aligns the content in a table row
5441  * 
5442  * @constructor
5443  * Create a new TableRow
5444  * @param {Object} config The config object
5445  */
5446
5447 Roo.bootstrap.TableRow = function(config){
5448     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
5449 };
5450
5451 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
5452     
5453     cls: false,
5454     align: false,
5455     bgcolor: false,
5456     charoff: false,
5457     valign: false,
5458     
5459     getAutoCreate : function(){
5460         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
5461         
5462         cfg = {
5463             tag: 'tr'
5464         }
5465             
5466         if(this.cls){
5467             cfg.cls = this.cls;
5468         }
5469         if(this.align){
5470             cfg.align = this.align;
5471         }
5472         if(this.bgcolor){
5473             cfg.bgcolor = this.bgcolor;
5474         }
5475         if(this.charoff){
5476             cfg.charoff = this.charoff;
5477         }
5478         if(this.valign){
5479             cfg.valign = this.valign;
5480         }
5481         
5482         return cfg;
5483     }
5484    
5485 });
5486
5487  
5488
5489  /*
5490  * - LGPL
5491  *
5492  * table body
5493  * 
5494  */
5495
5496 /**
5497  * @class Roo.bootstrap.TableBody
5498  * @extends Roo.bootstrap.Component
5499  * Bootstrap TableBody class
5500  * @cfg {String} cls element class
5501  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
5502  * @cfg {String} align Aligns the content inside the element
5503  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
5504  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
5505  * 
5506  * @constructor
5507  * Create a new TableBody
5508  * @param {Object} config The config object
5509  */
5510
5511 Roo.bootstrap.TableBody = function(config){
5512     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
5513 };
5514
5515 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
5516     
5517     cls: false,
5518     tag: false,
5519     align: false,
5520     charoff: false,
5521     valign: false,
5522     
5523     getAutoCreate : function(){
5524         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
5525         
5526         cfg = {
5527             tag: 'tbody'
5528         }
5529             
5530         if (this.cls) {
5531             cfg.cls=this.cls
5532         }
5533         if(this.tag){
5534             cfg.tag = this.tag;
5535         }
5536         
5537         if(this.align){
5538             cfg.align = this.align;
5539         }
5540         if(this.charoff){
5541             cfg.charoff = this.charoff;
5542         }
5543         if(this.valign){
5544             cfg.valign = this.valign;
5545         }
5546         
5547         return cfg;
5548     }
5549     
5550     
5551 //    initEvents : function()
5552 //    {
5553 //        
5554 //        if(!this.store){
5555 //            return;
5556 //        }
5557 //        
5558 //        this.store = Roo.factory(this.store, Roo.data);
5559 //        this.store.on('load', this.onLoad, this);
5560 //        
5561 //        this.store.load();
5562 //        
5563 //    },
5564 //    
5565 //    onLoad: function () 
5566 //    {   
5567 //        this.fireEvent('load', this);
5568 //    }
5569 //    
5570 //   
5571 });
5572
5573  
5574
5575  /*
5576  * Based on:
5577  * Ext JS Library 1.1.1
5578  * Copyright(c) 2006-2007, Ext JS, LLC.
5579  *
5580  * Originally Released Under LGPL - original licence link has changed is not relivant.
5581  *
5582  * Fork - LGPL
5583  * <script type="text/javascript">
5584  */
5585
5586 // as we use this in bootstrap.
5587 Roo.namespace('Roo.form');
5588  /**
5589  * @class Roo.form.Action
5590  * Internal Class used to handle form actions
5591  * @constructor
5592  * @param {Roo.form.BasicForm} el The form element or its id
5593  * @param {Object} config Configuration options
5594  */
5595
5596  
5597  
5598 // define the action interface
5599 Roo.form.Action = function(form, options){
5600     this.form = form;
5601     this.options = options || {};
5602 };
5603 /**
5604  * Client Validation Failed
5605  * @const 
5606  */
5607 Roo.form.Action.CLIENT_INVALID = 'client';
5608 /**
5609  * Server Validation Failed
5610  * @const 
5611  */
5612 Roo.form.Action.SERVER_INVALID = 'server';
5613  /**
5614  * Connect to Server Failed
5615  * @const 
5616  */
5617 Roo.form.Action.CONNECT_FAILURE = 'connect';
5618 /**
5619  * Reading Data from Server Failed
5620  * @const 
5621  */
5622 Roo.form.Action.LOAD_FAILURE = 'load';
5623
5624 Roo.form.Action.prototype = {
5625     type : 'default',
5626     failureType : undefined,
5627     response : undefined,
5628     result : undefined,
5629
5630     // interface method
5631     run : function(options){
5632
5633     },
5634
5635     // interface method
5636     success : function(response){
5637
5638     },
5639
5640     // interface method
5641     handleResponse : function(response){
5642
5643     },
5644
5645     // default connection failure
5646     failure : function(response){
5647         
5648         this.response = response;
5649         this.failureType = Roo.form.Action.CONNECT_FAILURE;
5650         this.form.afterAction(this, false);
5651     },
5652
5653     processResponse : function(response){
5654         this.response = response;
5655         if(!response.responseText){
5656             return true;
5657         }
5658         this.result = this.handleResponse(response);
5659         return this.result;
5660     },
5661
5662     // utility functions used internally
5663     getUrl : function(appendParams){
5664         var url = this.options.url || this.form.url || this.form.el.dom.action;
5665         if(appendParams){
5666             var p = this.getParams();
5667             if(p){
5668                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
5669             }
5670         }
5671         return url;
5672     },
5673
5674     getMethod : function(){
5675         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
5676     },
5677
5678     getParams : function(){
5679         var bp = this.form.baseParams;
5680         var p = this.options.params;
5681         if(p){
5682             if(typeof p == "object"){
5683                 p = Roo.urlEncode(Roo.applyIf(p, bp));
5684             }else if(typeof p == 'string' && bp){
5685                 p += '&' + Roo.urlEncode(bp);
5686             }
5687         }else if(bp){
5688             p = Roo.urlEncode(bp);
5689         }
5690         return p;
5691     },
5692
5693     createCallback : function(){
5694         return {
5695             success: this.success,
5696             failure: this.failure,
5697             scope: this,
5698             timeout: (this.form.timeout*1000),
5699             upload: this.form.fileUpload ? this.success : undefined
5700         };
5701     }
5702 };
5703
5704 Roo.form.Action.Submit = function(form, options){
5705     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
5706 };
5707
5708 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
5709     type : 'submit',
5710
5711     haveProgress : false,
5712     uploadComplete : false,
5713     
5714     // uploadProgress indicator.
5715     uploadProgress : function()
5716     {
5717         if (!this.form.progressUrl) {
5718             return;
5719         }
5720         
5721         if (!this.haveProgress) {
5722             Roo.MessageBox.progress("Uploading", "Uploading");
5723         }
5724         if (this.uploadComplete) {
5725            Roo.MessageBox.hide();
5726            return;
5727         }
5728         
5729         this.haveProgress = true;
5730    
5731         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
5732         
5733         var c = new Roo.data.Connection();
5734         c.request({
5735             url : this.form.progressUrl,
5736             params: {
5737                 id : uid
5738             },
5739             method: 'GET',
5740             success : function(req){
5741                //console.log(data);
5742                 var rdata = false;
5743                 var edata;
5744                 try  {
5745                    rdata = Roo.decode(req.responseText)
5746                 } catch (e) {
5747                     Roo.log("Invalid data from server..");
5748                     Roo.log(edata);
5749                     return;
5750                 }
5751                 if (!rdata || !rdata.success) {
5752                     Roo.log(rdata);
5753                     Roo.MessageBox.alert(Roo.encode(rdata));
5754                     return;
5755                 }
5756                 var data = rdata.data;
5757                 
5758                 if (this.uploadComplete) {
5759                    Roo.MessageBox.hide();
5760                    return;
5761                 }
5762                    
5763                 if (data){
5764                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
5765                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
5766                     );
5767                 }
5768                 this.uploadProgress.defer(2000,this);
5769             },
5770        
5771             failure: function(data) {
5772                 Roo.log('progress url failed ');
5773                 Roo.log(data);
5774             },
5775             scope : this
5776         });
5777            
5778     },
5779     
5780     
5781     run : function()
5782     {
5783         // run get Values on the form, so it syncs any secondary forms.
5784         this.form.getValues();
5785         
5786         var o = this.options;
5787         var method = this.getMethod();
5788         var isPost = method == 'POST';
5789         if(o.clientValidation === false || this.form.isValid()){
5790             
5791             if (this.form.progressUrl) {
5792                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
5793                     (new Date() * 1) + '' + Math.random());
5794                     
5795             } 
5796             
5797             
5798             Roo.Ajax.request(Roo.apply(this.createCallback(), {
5799                 form:this.form.el.dom,
5800                 url:this.getUrl(!isPost),
5801                 method: method,
5802                 params:isPost ? this.getParams() : null,
5803                 isUpload: this.form.fileUpload
5804             }));
5805             
5806             this.uploadProgress();
5807
5808         }else if (o.clientValidation !== false){ // client validation failed
5809             this.failureType = Roo.form.Action.CLIENT_INVALID;
5810             this.form.afterAction(this, false);
5811         }
5812     },
5813
5814     success : function(response)
5815     {
5816         this.uploadComplete= true;
5817         if (this.haveProgress) {
5818             Roo.MessageBox.hide();
5819         }
5820         
5821         
5822         var result = this.processResponse(response);
5823         if(result === true || result.success){
5824             this.form.afterAction(this, true);
5825             return;
5826         }
5827         if(result.errors){
5828             this.form.markInvalid(result.errors);
5829             this.failureType = Roo.form.Action.SERVER_INVALID;
5830         }
5831         this.form.afterAction(this, false);
5832     },
5833     failure : function(response)
5834     {
5835         this.uploadComplete= true;
5836         if (this.haveProgress) {
5837             Roo.MessageBox.hide();
5838         }
5839         
5840         this.response = response;
5841         this.failureType = Roo.form.Action.CONNECT_FAILURE;
5842         this.form.afterAction(this, false);
5843     },
5844     
5845     handleResponse : function(response){
5846         if(this.form.errorReader){
5847             var rs = this.form.errorReader.read(response);
5848             var errors = [];
5849             if(rs.records){
5850                 for(var i = 0, len = rs.records.length; i < len; i++) {
5851                     var r = rs.records[i];
5852                     errors[i] = r.data;
5853                 }
5854             }
5855             if(errors.length < 1){
5856                 errors = null;
5857             }
5858             return {
5859                 success : rs.success,
5860                 errors : errors
5861             };
5862         }
5863         var ret = false;
5864         try {
5865             ret = Roo.decode(response.responseText);
5866         } catch (e) {
5867             ret = {
5868                 success: false,
5869                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
5870                 errors : []
5871             };
5872         }
5873         return ret;
5874         
5875     }
5876 });
5877
5878
5879 Roo.form.Action.Load = function(form, options){
5880     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
5881     this.reader = this.form.reader;
5882 };
5883
5884 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
5885     type : 'load',
5886
5887     run : function(){
5888         
5889         Roo.Ajax.request(Roo.apply(
5890                 this.createCallback(), {
5891                     method:this.getMethod(),
5892                     url:this.getUrl(false),
5893                     params:this.getParams()
5894         }));
5895     },
5896
5897     success : function(response){
5898         
5899         var result = this.processResponse(response);
5900         if(result === true || !result.success || !result.data){
5901             this.failureType = Roo.form.Action.LOAD_FAILURE;
5902             this.form.afterAction(this, false);
5903             return;
5904         }
5905         this.form.clearInvalid();
5906         this.form.setValues(result.data);
5907         this.form.afterAction(this, true);
5908     },
5909
5910     handleResponse : function(response){
5911         if(this.form.reader){
5912             var rs = this.form.reader.read(response);
5913             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
5914             return {
5915                 success : rs.success,
5916                 data : data
5917             };
5918         }
5919         return Roo.decode(response.responseText);
5920     }
5921 });
5922
5923 Roo.form.Action.ACTION_TYPES = {
5924     'load' : Roo.form.Action.Load,
5925     'submit' : Roo.form.Action.Submit
5926 };/*
5927  * - LGPL
5928  *
5929  * form
5930  * 
5931  */
5932
5933 /**
5934  * @class Roo.bootstrap.Form
5935  * @extends Roo.bootstrap.Component
5936  * Bootstrap Form class
5937  * @cfg {String} method  GET | POST (default POST)
5938  * @cfg {String} labelAlign top | left (default top)
5939   * @cfg {String} align left  | right - for navbars
5940
5941  * 
5942  * @constructor
5943  * Create a new Form
5944  * @param {Object} config The config object
5945  */
5946
5947
5948 Roo.bootstrap.Form = function(config){
5949     Roo.bootstrap.Form.superclass.constructor.call(this, config);
5950     this.addEvents({
5951         /**
5952          * @event clientvalidation
5953          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
5954          * @param {Form} this
5955          * @param {Boolean} valid true if the form has passed client-side validation
5956          */
5957         clientvalidation: true,
5958         /**
5959          * @event beforeaction
5960          * Fires before any action is performed. Return false to cancel the action.
5961          * @param {Form} this
5962          * @param {Action} action The action to be performed
5963          */
5964         beforeaction: true,
5965         /**
5966          * @event actionfailed
5967          * Fires when an action fails.
5968          * @param {Form} this
5969          * @param {Action} action The action that failed
5970          */
5971         actionfailed : true,
5972         /**
5973          * @event actioncomplete
5974          * Fires when an action is completed.
5975          * @param {Form} this
5976          * @param {Action} action The action that completed
5977          */
5978         actioncomplete : true
5979     });
5980     
5981 };
5982
5983 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
5984       
5985      /**
5986      * @cfg {String} method
5987      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
5988      */
5989     method : 'POST',
5990     /**
5991      * @cfg {String} url
5992      * The URL to use for form actions if one isn't supplied in the action options.
5993      */
5994     /**
5995      * @cfg {Boolean} fileUpload
5996      * Set to true if this form is a file upload.
5997      */
5998      
5999     /**
6000      * @cfg {Object} baseParams
6001      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
6002      */
6003       
6004     /**
6005      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
6006      */
6007     timeout: 30,
6008     /**
6009      * @cfg {Sting} align (left|right) for navbar forms
6010      */
6011     align : 'left',
6012
6013     // private
6014     activeAction : null,
6015  
6016     /**
6017      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6018      * element by passing it or its id or mask the form itself by passing in true.
6019      * @type Mixed
6020      */
6021     waitMsgTarget : false,
6022     
6023      
6024     
6025     /**
6026      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6027      * element by passing it or its id or mask the form itself by passing in true.
6028      * @type Mixed
6029      */
6030     
6031     getAutoCreate : function(){
6032         
6033         var cfg = {
6034             tag: 'form',
6035             method : this.method || 'POST',
6036             id : this.id || Roo.id(),
6037             cls : ''
6038         }
6039         if (this.parent().xtype.match(/^Nav/)) {
6040             cfg.cls = 'navbar-form navbar-' + this.align;
6041             
6042         }
6043         
6044         if (this.labelAlign == 'left' ) {
6045             cfg.cls += ' form-horizontal';
6046         }
6047         
6048         
6049         return cfg;
6050     },
6051     initEvents : function()
6052     {
6053         this.el.on('submit', this.onSubmit, this);
6054         // this was added as random key presses on the form where triggering form submit.
6055         this.el.on('keypress', function(e) {
6056             if (e.getCharCode() != 13) {
6057                 return true;
6058             }
6059             // we might need to allow it for textareas.. and some other items.
6060             // check e.getTarget().
6061             
6062             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
6063                 return true;
6064             }
6065         
6066             Roo.log("keypress blocked");
6067             
6068             e.preventDefault();
6069             return false;
6070         });
6071         
6072     },
6073     // private
6074     onSubmit : function(e){
6075         e.stopEvent();
6076     },
6077     
6078      /**
6079      * Returns true if client-side validation on the form is successful.
6080      * @return Boolean
6081      */
6082     isValid : function(){
6083         var items = this.getItems();
6084         var valid = true;
6085         items.each(function(f){
6086            if(!f.validate()){
6087                valid = false;
6088                
6089            }
6090         });
6091         return valid;
6092     },
6093     /**
6094      * Returns true if any fields in this form have changed since their original load.
6095      * @return Boolean
6096      */
6097     isDirty : function(){
6098         var dirty = false;
6099         var items = this.getItems();
6100         items.each(function(f){
6101            if(f.isDirty()){
6102                dirty = true;
6103                return false;
6104            }
6105            return true;
6106         });
6107         return dirty;
6108     },
6109      /**
6110      * Performs a predefined action (submit or load) or custom actions you define on this form.
6111      * @param {String} actionName The name of the action type
6112      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
6113      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
6114      * accept other config options):
6115      * <pre>
6116 Property          Type             Description
6117 ----------------  ---------------  ----------------------------------------------------------------------------------
6118 url               String           The url for the action (defaults to the form's url)
6119 method            String           The form method to use (defaults to the form's method, or POST if not defined)
6120 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
6121 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
6122                                    validate the form on the client (defaults to false)
6123      * </pre>
6124      * @return {BasicForm} this
6125      */
6126     doAction : function(action, options){
6127         if(typeof action == 'string'){
6128             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
6129         }
6130         if(this.fireEvent('beforeaction', this, action) !== false){
6131             this.beforeAction(action);
6132             action.run.defer(100, action);
6133         }
6134         return this;
6135     },
6136     
6137     // private
6138     beforeAction : function(action){
6139         var o = action.options;
6140         
6141         // not really supported yet.. ??
6142         
6143         //if(this.waitMsgTarget === true){
6144             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6145         //}else if(this.waitMsgTarget){
6146         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
6147         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
6148         //}else {
6149         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
6150        // }
6151          
6152     },
6153
6154     // private
6155     afterAction : function(action, success){
6156         this.activeAction = null;
6157         var o = action.options;
6158         
6159         //if(this.waitMsgTarget === true){
6160             this.el.unmask();
6161         //}else if(this.waitMsgTarget){
6162         //    this.waitMsgTarget.unmask();
6163         //}else{
6164         //    Roo.MessageBox.updateProgress(1);
6165         //    Roo.MessageBox.hide();
6166        // }
6167         // 
6168         if(success){
6169             if(o.reset){
6170                 this.reset();
6171             }
6172             Roo.callback(o.success, o.scope, [this, action]);
6173             this.fireEvent('actioncomplete', this, action);
6174             
6175         }else{
6176             
6177             // failure condition..
6178             // we have a scenario where updates need confirming.
6179             // eg. if a locking scenario exists..
6180             // we look for { errors : { needs_confirm : true }} in the response.
6181             if (
6182                 (typeof(action.result) != 'undefined')  &&
6183                 (typeof(action.result.errors) != 'undefined')  &&
6184                 (typeof(action.result.errors.needs_confirm) != 'undefined')
6185            ){
6186                 var _t = this;
6187                 Roo.log("not supported yet");
6188                  /*
6189                 
6190                 Roo.MessageBox.confirm(
6191                     "Change requires confirmation",
6192                     action.result.errorMsg,
6193                     function(r) {
6194                         if (r != 'yes') {
6195                             return;
6196                         }
6197                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
6198                     }
6199                     
6200                 );
6201                 */
6202                 
6203                 
6204                 return;
6205             }
6206             
6207             Roo.callback(o.failure, o.scope, [this, action]);
6208             // show an error message if no failed handler is set..
6209             if (!this.hasListener('actionfailed')) {
6210                 Roo.log("need to add dialog support");
6211                 /*
6212                 Roo.MessageBox.alert("Error",
6213                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
6214                         action.result.errorMsg :
6215                         "Saving Failed, please check your entries or try again"
6216                 );
6217                 */
6218             }
6219             
6220             this.fireEvent('actionfailed', this, action);
6221         }
6222         
6223     },
6224     /**
6225      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
6226      * @param {String} id The value to search for
6227      * @return Field
6228      */
6229     findField : function(id){
6230         var items = this.getItems();
6231         var field = items.get(id);
6232         if(!field){
6233              items.each(function(f){
6234                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
6235                     field = f;
6236                     return false;
6237                 }
6238                 return true;
6239             });
6240         }
6241         return field || null;
6242     },
6243      /**
6244      * Mark fields in this form invalid in bulk.
6245      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
6246      * @return {BasicForm} this
6247      */
6248     markInvalid : function(errors){
6249         if(errors instanceof Array){
6250             for(var i = 0, len = errors.length; i < len; i++){
6251                 var fieldError = errors[i];
6252                 var f = this.findField(fieldError.id);
6253                 if(f){
6254                     f.markInvalid(fieldError.msg);
6255                 }
6256             }
6257         }else{
6258             var field, id;
6259             for(id in errors){
6260                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
6261                     field.markInvalid(errors[id]);
6262                 }
6263             }
6264         }
6265         //Roo.each(this.childForms || [], function (f) {
6266         //    f.markInvalid(errors);
6267         //});
6268         
6269         return this;
6270     },
6271
6272     /**
6273      * Set values for fields in this form in bulk.
6274      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
6275      * @return {BasicForm} this
6276      */
6277     setValues : function(values){
6278         if(values instanceof Array){ // array of objects
6279             for(var i = 0, len = values.length; i < len; i++){
6280                 var v = values[i];
6281                 var f = this.findField(v.id);
6282                 if(f){
6283                     f.setValue(v.value);
6284                     if(this.trackResetOnLoad){
6285                         f.originalValue = f.getValue();
6286                     }
6287                 }
6288             }
6289         }else{ // object hash
6290             var field, id;
6291             for(id in values){
6292                 if(typeof values[id] != 'function' && (field = this.findField(id))){
6293                     
6294                     if (field.setFromData && 
6295                         field.valueField && 
6296                         field.displayField &&
6297                         // combos' with local stores can 
6298                         // be queried via setValue()
6299                         // to set their value..
6300                         (field.store && !field.store.isLocal)
6301                         ) {
6302                         // it's a combo
6303                         var sd = { };
6304                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
6305                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
6306                         field.setFromData(sd);
6307                         
6308                     } else {
6309                         field.setValue(values[id]);
6310                     }
6311                     
6312                     
6313                     if(this.trackResetOnLoad){
6314                         field.originalValue = field.getValue();
6315                     }
6316                 }
6317             }
6318         }
6319          
6320         //Roo.each(this.childForms || [], function (f) {
6321         //    f.setValues(values);
6322         //});
6323                 
6324         return this;
6325     },
6326
6327     /**
6328      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
6329      * they are returned as an array.
6330      * @param {Boolean} asString
6331      * @return {Object}
6332      */
6333     getValues : function(asString){
6334         //if (this.childForms) {
6335             // copy values from the child forms
6336         //    Roo.each(this.childForms, function (f) {
6337         //        this.setValues(f.getValues());
6338         //    }, this);
6339         //}
6340         
6341         
6342         
6343         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
6344         if(asString === true){
6345             return fs;
6346         }
6347         return Roo.urlDecode(fs);
6348     },
6349     
6350     /**
6351      * Returns the fields in this form as an object with key/value pairs. 
6352      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
6353      * @return {Object}
6354      */
6355     getFieldValues : function(with_hidden)
6356     {
6357         var items = this.getItems();
6358         var ret = {};
6359         items.each(function(f){
6360             if (!f.getName()) {
6361                 return;
6362             }
6363             var v = f.getValue();
6364             if (f.inputType =='radio') {
6365                 if (typeof(ret[f.getName()]) == 'undefined') {
6366                     ret[f.getName()] = ''; // empty..
6367                 }
6368                 
6369                 if (!f.el.dom.checked) {
6370                     return;
6371                     
6372                 }
6373                 v = f.el.dom.value;
6374                 
6375             }
6376             
6377             // not sure if this supported any more..
6378             if ((typeof(v) == 'object') && f.getRawValue) {
6379                 v = f.getRawValue() ; // dates..
6380             }
6381             // combo boxes where name != hiddenName...
6382             if (f.name != f.getName()) {
6383                 ret[f.name] = f.getRawValue();
6384             }
6385             ret[f.getName()] = v;
6386         });
6387         
6388         return ret;
6389     },
6390
6391     /**
6392      * Clears all invalid messages in this form.
6393      * @return {BasicForm} this
6394      */
6395     clearInvalid : function(){
6396         var items = this.getItems();
6397         
6398         items.each(function(f){
6399            f.clearInvalid();
6400         });
6401         
6402         
6403         
6404         return this;
6405     },
6406
6407     /**
6408      * Resets this form.
6409      * @return {BasicForm} this
6410      */
6411     reset : function(){
6412         var items = this.getItems();
6413         items.each(function(f){
6414             f.reset();
6415         });
6416         
6417         Roo.each(this.childForms || [], function (f) {
6418             f.reset();
6419         });
6420        
6421         
6422         return this;
6423     },
6424     getItems : function()
6425     {
6426         var r=new Roo.util.MixedCollection(false, function(o){
6427             return o.id || (o.id = Roo.id());
6428         });
6429         var iter = function(el) {
6430             if (el.inputEl) {
6431                 r.add(el);
6432             }
6433             if (!el.items) {
6434                 return;
6435             }
6436             Roo.each(el.items,function(e) {
6437                 iter(e);
6438             });
6439             
6440             
6441         };
6442         iter(this);
6443         return r;
6444         
6445         
6446         
6447         
6448     }
6449     
6450 });
6451
6452  
6453 /*
6454  * Based on:
6455  * Ext JS Library 1.1.1
6456  * Copyright(c) 2006-2007, Ext JS, LLC.
6457  *
6458  * Originally Released Under LGPL - original licence link has changed is not relivant.
6459  *
6460  * Fork - LGPL
6461  * <script type="text/javascript">
6462  */
6463 /**
6464  * @class Roo.form.VTypes
6465  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
6466  * @singleton
6467  */
6468 Roo.form.VTypes = function(){
6469     // closure these in so they are only created once.
6470     var alpha = /^[a-zA-Z_]+$/;
6471     var alphanum = /^[a-zA-Z0-9_]+$/;
6472     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
6473     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
6474
6475     // All these messages and functions are configurable
6476     return {
6477         /**
6478          * The function used to validate email addresses
6479          * @param {String} value The email address
6480          */
6481         'email' : function(v){
6482             return email.test(v);
6483         },
6484         /**
6485          * The error text to display when the email validation function returns false
6486          * @type String
6487          */
6488         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
6489         /**
6490          * The keystroke filter mask to be applied on email input
6491          * @type RegExp
6492          */
6493         'emailMask' : /[a-z0-9_\.\-@]/i,
6494
6495         /**
6496          * The function used to validate URLs
6497          * @param {String} value The URL
6498          */
6499         'url' : function(v){
6500             return url.test(v);
6501         },
6502         /**
6503          * The error text to display when the url validation function returns false
6504          * @type String
6505          */
6506         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
6507         
6508         /**
6509          * The function used to validate alpha values
6510          * @param {String} value The value
6511          */
6512         'alpha' : function(v){
6513             return alpha.test(v);
6514         },
6515         /**
6516          * The error text to display when the alpha validation function returns false
6517          * @type String
6518          */
6519         'alphaText' : 'This field should only contain letters and _',
6520         /**
6521          * The keystroke filter mask to be applied on alpha input
6522          * @type RegExp
6523          */
6524         'alphaMask' : /[a-z_]/i,
6525
6526         /**
6527          * The function used to validate alphanumeric values
6528          * @param {String} value The value
6529          */
6530         'alphanum' : function(v){
6531             return alphanum.test(v);
6532         },
6533         /**
6534          * The error text to display when the alphanumeric validation function returns false
6535          * @type String
6536          */
6537         'alphanumText' : 'This field should only contain letters, numbers and _',
6538         /**
6539          * The keystroke filter mask to be applied on alphanumeric input
6540          * @type RegExp
6541          */
6542         'alphanumMask' : /[a-z0-9_]/i
6543     };
6544 }();/*
6545  * - LGPL
6546  *
6547  * Input
6548  * 
6549  */
6550
6551 /**
6552  * @class Roo.bootstrap.Input
6553  * @extends Roo.bootstrap.Component
6554  * Bootstrap Input class
6555  * @cfg {Boolean} disabled is it disabled
6556  * @cfg {String} fieldLabel - the label associated
6557  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
6558  * @cfg {String} name name of the input
6559  * @cfg {string} fieldLabel - the label associated
6560  * @cfg {string}  inputType - input / file submit ...
6561  * @cfg {string} placeholder - placeholder to put in text.
6562  * @cfg {string}  before - input group add on before
6563  * @cfg {string} after - input group add on after
6564  * @cfg {string} size - (lg|sm) or leave empty..
6565  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
6566  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
6567  * @cfg {Number} md colspan out of 12 for computer-sized screens
6568  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
6569  * @cfg {string} value default value of the input
6570  * @cfg {Number} labelWidth set the width of label (0-12)
6571  * @cfg {String} labelAlign (top|left)
6572  * @cfg {Boolean} readOnly Specifies that the field should be read-only
6573  * @cfg {String} align (left|center|right) Default left
6574  * 
6575  * 
6576  * @constructor
6577  * Create a new Input
6578  * @param {Object} config The config object
6579  */
6580
6581 Roo.bootstrap.Input = function(config){
6582     Roo.bootstrap.Input.superclass.constructor.call(this, config);
6583    
6584         this.addEvents({
6585             /**
6586              * @event focus
6587              * Fires when this field receives input focus.
6588              * @param {Roo.form.Field} this
6589              */
6590             focus : true,
6591             /**
6592              * @event blur
6593              * Fires when this field loses input focus.
6594              * @param {Roo.form.Field} this
6595              */
6596             blur : true,
6597             /**
6598              * @event specialkey
6599              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
6600              * {@link Roo.EventObject#getKey} to determine which key was pressed.
6601              * @param {Roo.form.Field} this
6602              * @param {Roo.EventObject} e The event object
6603              */
6604             specialkey : true,
6605             /**
6606              * @event change
6607              * Fires just before the field blurs if the field value has changed.
6608              * @param {Roo.form.Field} this
6609              * @param {Mixed} newValue The new value
6610              * @param {Mixed} oldValue The original value
6611              */
6612             change : true,
6613             /**
6614              * @event invalid
6615              * Fires after the field has been marked as invalid.
6616              * @param {Roo.form.Field} this
6617              * @param {String} msg The validation message
6618              */
6619             invalid : true,
6620             /**
6621              * @event valid
6622              * Fires after the field has been validated with no errors.
6623              * @param {Roo.form.Field} this
6624              */
6625             valid : true,
6626              /**
6627              * @event keyup
6628              * Fires after the key up
6629              * @param {Roo.form.Field} this
6630              * @param {Roo.EventObject}  e The event Object
6631              */
6632             keyup : true
6633         });
6634 };
6635
6636 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
6637      /**
6638      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
6639       automatic validation (defaults to "keyup").
6640      */
6641     validationEvent : "keyup",
6642      /**
6643      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
6644      */
6645     validateOnBlur : true,
6646     /**
6647      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
6648      */
6649     validationDelay : 250,
6650      /**
6651      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
6652      */
6653     focusClass : "x-form-focus",  // not needed???
6654     
6655        
6656     /**
6657      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
6658      */
6659     invalidClass : "has-error",
6660     
6661     /**
6662      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
6663      */
6664     selectOnFocus : false,
6665     
6666      /**
6667      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
6668      */
6669     maskRe : null,
6670        /**
6671      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
6672      */
6673     vtype : null,
6674     
6675       /**
6676      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
6677      */
6678     disableKeyFilter : false,
6679     
6680        /**
6681      * @cfg {Boolean} disabled True to disable the field (defaults to false).
6682      */
6683     disabled : false,
6684      /**
6685      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
6686      */
6687     allowBlank : true,
6688     /**
6689      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
6690      */
6691     blankText : "This field is required",
6692     
6693      /**
6694      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
6695      */
6696     minLength : 0,
6697     /**
6698      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
6699      */
6700     maxLength : Number.MAX_VALUE,
6701     /**
6702      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
6703      */
6704     minLengthText : "The minimum length for this field is {0}",
6705     /**
6706      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
6707      */
6708     maxLengthText : "The maximum length for this field is {0}",
6709   
6710     
6711     /**
6712      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
6713      * If available, this function will be called only after the basic validators all return true, and will be passed the
6714      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
6715      */
6716     validator : null,
6717     /**
6718      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
6719      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
6720      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
6721      */
6722     regex : null,
6723     /**
6724      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
6725      */
6726     regexText : "",
6727     
6728     
6729     
6730     fieldLabel : '',
6731     inputType : 'text',
6732     
6733     name : false,
6734     placeholder: false,
6735     before : false,
6736     after : false,
6737     size : false,
6738     // private
6739     hasFocus : false,
6740     preventMark: false,
6741     isFormField : true,
6742     value : '',
6743     labelWidth : 2,
6744     labelAlign : false,
6745     readOnly : false,
6746     align : false,
6747     formatedValue : false,
6748     
6749     parentLabelAlign : function()
6750     {
6751         var parent = this;
6752         while (parent.parent()) {
6753             parent = parent.parent();
6754             if (typeof(parent.labelAlign) !='undefined') {
6755                 return parent.labelAlign;
6756             }
6757         }
6758         return 'left';
6759         
6760     },
6761     
6762     getAutoCreate : function(){
6763         
6764         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
6765         
6766         var id = Roo.id();
6767         
6768         var cfg = {};
6769         
6770         if(this.inputType != 'hidden'){
6771             cfg.cls = 'form-group' //input-group
6772         }
6773         
6774         var input =  {
6775             tag: 'input',
6776             id : id,
6777             type : this.inputType,
6778             value : this.value,
6779             cls : 'form-control',
6780             placeholder : this.placeholder || ''
6781             
6782         };
6783         
6784         if(this.align){
6785             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
6786         }
6787         
6788         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
6789             input.maxLength = this.maxLength;
6790         }
6791         
6792         if (this.disabled) {
6793             input.disabled=true;
6794         }
6795         
6796         if (this.readOnly) {
6797             input.readonly=true;
6798         }
6799         
6800         if (this.name) {
6801             input.name = this.name;
6802         }
6803         if (this.size) {
6804             input.cls += ' input-' + this.size;
6805         }
6806         var settings=this;
6807         ['xs','sm','md','lg'].map(function(size){
6808             if (settings[size]) {
6809                 cfg.cls += ' col-' + size + '-' + settings[size];
6810             }
6811         });
6812         
6813         var inputblock = input;
6814         
6815         if (this.before || this.after) {
6816             
6817             inputblock = {
6818                 cls : 'input-group',
6819                 cn :  [] 
6820             };
6821             if (this.before && typeof(this.before) == 'string') {
6822                 
6823                 inputblock.cn.push({
6824                     tag :'span',
6825                     cls : 'roo-input-before input-group-addon',
6826                     html : this.before
6827                 });
6828             }
6829             if (this.before && typeof(this.before) == 'object') {
6830                 this.before = Roo.factory(this.before);
6831                 Roo.log(this.before);
6832                 inputblock.cn.push({
6833                     tag :'span',
6834                     cls : 'roo-input-before input-group-' +
6835                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
6836                 });
6837             }
6838             
6839             inputblock.cn.push(input);
6840             
6841             if (this.after && typeof(this.after) == 'string') {
6842                 inputblock.cn.push({
6843                     tag :'span',
6844                     cls : 'roo-input-after input-group-addon',
6845                     html : this.after
6846                 });
6847             }
6848             if (this.after && typeof(this.after) == 'object') {
6849                 this.after = Roo.factory(this.after);
6850                 Roo.log(this.after);
6851                 inputblock.cn.push({
6852                     tag :'span',
6853                     cls : 'roo-input-after input-group-' +
6854                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
6855                 });
6856             }
6857         };
6858         
6859         if (align ==='left' && this.fieldLabel.length) {
6860                 Roo.log("left and has label");
6861                 cfg.cn = [
6862                     
6863                     {
6864                         tag: 'label',
6865                         'for' :  id,
6866                         cls : 'control-label col-sm-' + this.labelWidth,
6867                         html : this.fieldLabel
6868                         
6869                     },
6870                     {
6871                         cls : "col-sm-" + (12 - this.labelWidth), 
6872                         cn: [
6873                             inputblock
6874                         ]
6875                     }
6876                     
6877                 ];
6878         } else if ( this.fieldLabel.length) {
6879                 Roo.log(" label");
6880                  cfg.cn = [
6881                    
6882                     {
6883                         tag: 'label',
6884                         //cls : 'input-group-addon',
6885                         html : this.fieldLabel
6886                         
6887                     },
6888                     
6889                     inputblock
6890                     
6891                 ];
6892
6893         } else {
6894             
6895                 Roo.log(" no label && no align");
6896                 cfg.cn = [
6897                     
6898                         inputblock
6899                     
6900                 ];
6901                 
6902                 
6903         };
6904         Roo.log('input-parentType: ' + this.parentType);
6905         
6906         if (this.parentType === 'Navbar' &&  this.parent().bar) {
6907            cfg.cls += ' navbar-form';
6908            Roo.log(cfg);
6909         }
6910         
6911         return cfg;
6912         
6913     },
6914     /**
6915      * return the real input element.
6916      */
6917     inputEl: function ()
6918     {
6919         return this.el.select('input.form-control',true).first();
6920     },
6921     setDisabled : function(v)
6922     {
6923         var i  = this.inputEl().dom;
6924         if (!v) {
6925             i.removeAttribute('disabled');
6926             return;
6927             
6928         }
6929         i.setAttribute('disabled','true');
6930     },
6931     initEvents : function()
6932     {
6933         
6934         this.inputEl().on("keydown" , this.fireKey,  this);
6935         this.inputEl().on("focus", this.onFocus,  this);
6936         this.inputEl().on("blur", this.onBlur,  this);
6937         
6938         this.inputEl().relayEvent('keyup', this);
6939
6940         // reference to original value for reset
6941         this.originalValue = this.getValue();
6942         //Roo.form.TextField.superclass.initEvents.call(this);
6943         if(this.validationEvent == 'keyup'){
6944             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
6945             this.inputEl().on('keyup', this.filterValidation, this);
6946         }
6947         else if(this.validationEvent !== false){
6948             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
6949         }
6950         
6951         if(this.selectOnFocus){
6952             this.on("focus", this.preFocus, this);
6953             
6954         }
6955         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
6956             this.inputEl().on("keypress", this.filterKeys, this);
6957         }
6958        /* if(this.grow){
6959             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
6960             this.el.on("click", this.autoSize,  this);
6961         }
6962         */
6963         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
6964             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
6965         }
6966         
6967         if (typeof(this.before) == 'object') {
6968             this.before.render(this.el.select('.roo-input-before',true).first());
6969         }
6970         if (typeof(this.after) == 'object') {
6971             this.after.render(this.el.select('.roo-input-after',true).first());
6972         }
6973         
6974         
6975     },
6976     filterValidation : function(e){
6977         if(!e.isNavKeyPress()){
6978             this.validationTask.delay(this.validationDelay);
6979         }
6980     },
6981      /**
6982      * Validates the field value
6983      * @return {Boolean} True if the value is valid, else false
6984      */
6985     validate : function(){
6986         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
6987         if(this.disabled || this.validateValue(this.getRawValue())){
6988             this.clearInvalid();
6989             return true;
6990         }
6991         return false;
6992     },
6993     
6994     
6995     /**
6996      * Validates a value according to the field's validation rules and marks the field as invalid
6997      * if the validation fails
6998      * @param {Mixed} value The value to validate
6999      * @return {Boolean} True if the value is valid, else false
7000      */
7001     validateValue : function(value){
7002         if(value.length < 1)  { // if it's blank
7003              if(this.allowBlank){
7004                 this.clearInvalid();
7005                 return true;
7006              }else{
7007                 this.markInvalid(this.blankText);
7008                 return false;
7009              }
7010         }
7011         if(value.length < this.minLength){
7012             this.markInvalid(String.format(this.minLengthText, this.minLength));
7013             return false;
7014         }
7015         if(value.length > this.maxLength){
7016             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
7017             return false;
7018         }
7019         if(this.vtype){
7020             var vt = Roo.form.VTypes;
7021             if(!vt[this.vtype](value, this)){
7022                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
7023                 return false;
7024             }
7025         }
7026         if(typeof this.validator == "function"){
7027             var msg = this.validator(value);
7028             if(msg !== true){
7029                 this.markInvalid(msg);
7030                 return false;
7031             }
7032         }
7033         if(this.regex && !this.regex.test(value)){
7034             this.markInvalid(this.regexText);
7035             return false;
7036         }
7037         return true;
7038     },
7039
7040     
7041     
7042      // private
7043     fireKey : function(e){
7044         //Roo.log('field ' + e.getKey());
7045         if(e.isNavKeyPress()){
7046             this.fireEvent("specialkey", this, e);
7047         }
7048     },
7049     focus : function (selectText){
7050         if(this.rendered){
7051             this.inputEl().focus();
7052             if(selectText === true){
7053                 this.inputEl().dom.select();
7054             }
7055         }
7056         return this;
7057     } ,
7058     
7059     onFocus : function(){
7060         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7061            // this.el.addClass(this.focusClass);
7062         }
7063         if(!this.hasFocus){
7064             this.hasFocus = true;
7065             this.startValue = this.getValue();
7066             this.fireEvent("focus", this);
7067         }
7068     },
7069     
7070     beforeBlur : Roo.emptyFn,
7071
7072     
7073     // private
7074     onBlur : function(){
7075         this.beforeBlur();
7076         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7077             //this.el.removeClass(this.focusClass);
7078         }
7079         this.hasFocus = false;
7080         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
7081             this.validate();
7082         }
7083         var v = this.getValue();
7084         if(String(v) !== String(this.startValue)){
7085             this.fireEvent('change', this, v, this.startValue);
7086         }
7087         this.fireEvent("blur", this);
7088     },
7089     
7090     /**
7091      * Resets the current field value to the originally loaded value and clears any validation messages
7092      */
7093     reset : function(){
7094         this.setValue(this.originalValue);
7095         this.clearInvalid();
7096     },
7097      /**
7098      * Returns the name of the field
7099      * @return {Mixed} name The name field
7100      */
7101     getName: function(){
7102         return this.name;
7103     },
7104      /**
7105      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
7106      * @return {Mixed} value The field value
7107      */
7108     getValue : function(){
7109         
7110         var v = this.inputEl().getValue();
7111         
7112         return v;
7113     },
7114     /**
7115      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
7116      * @return {Mixed} value The field value
7117      */
7118     getRawValue : function(){
7119         var v = this.inputEl().getValue();
7120         
7121         return v;
7122     },
7123     
7124     /**
7125      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
7126      * @param {Mixed} value The value to set
7127      */
7128     setRawValue : function(v){
7129         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7130     },
7131     
7132     selectText : function(start, end){
7133         var v = this.getRawValue();
7134         if(v.length > 0){
7135             start = start === undefined ? 0 : start;
7136             end = end === undefined ? v.length : end;
7137             var d = this.inputEl().dom;
7138             if(d.setSelectionRange){
7139                 d.setSelectionRange(start, end);
7140             }else if(d.createTextRange){
7141                 var range = d.createTextRange();
7142                 range.moveStart("character", start);
7143                 range.moveEnd("character", v.length-end);
7144                 range.select();
7145             }
7146         }
7147     },
7148     
7149     /**
7150      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
7151      * @param {Mixed} value The value to set
7152      */
7153     setValue : function(v){
7154         this.value = v;
7155         if(this.rendered){
7156             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7157             this.validate();
7158         }
7159     },
7160     
7161     /*
7162     processValue : function(value){
7163         if(this.stripCharsRe){
7164             var newValue = value.replace(this.stripCharsRe, '');
7165             if(newValue !== value){
7166                 this.setRawValue(newValue);
7167                 return newValue;
7168             }
7169         }
7170         return value;
7171     },
7172   */
7173     preFocus : function(){
7174         
7175         if(this.selectOnFocus){
7176             this.inputEl().dom.select();
7177         }
7178     },
7179     filterKeys : function(e){
7180         var k = e.getKey();
7181         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
7182             return;
7183         }
7184         var c = e.getCharCode(), cc = String.fromCharCode(c);
7185         if(Roo.isIE && (e.isSpecialKey() || !cc)){
7186             return;
7187         }
7188         if(!this.maskRe.test(cc)){
7189             e.stopEvent();
7190         }
7191     },
7192      /**
7193      * Clear any invalid styles/messages for this field
7194      */
7195     clearInvalid : function(){
7196         
7197         if(!this.el || this.preventMark){ // not rendered
7198             return;
7199         }
7200         this.el.removeClass(this.invalidClass);
7201         /*
7202         switch(this.msgTarget){
7203             case 'qtip':
7204                 this.el.dom.qtip = '';
7205                 break;
7206             case 'title':
7207                 this.el.dom.title = '';
7208                 break;
7209             case 'under':
7210                 if(this.errorEl){
7211                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
7212                 }
7213                 break;
7214             case 'side':
7215                 if(this.errorIcon){
7216                     this.errorIcon.dom.qtip = '';
7217                     this.errorIcon.hide();
7218                     this.un('resize', this.alignErrorIcon, this);
7219                 }
7220                 break;
7221             default:
7222                 var t = Roo.getDom(this.msgTarget);
7223                 t.innerHTML = '';
7224                 t.style.display = 'none';
7225                 break;
7226         }
7227         */
7228         this.fireEvent('valid', this);
7229     },
7230      /**
7231      * Mark this field as invalid
7232      * @param {String} msg The validation message
7233      */
7234     markInvalid : function(msg){
7235         if(!this.el  || this.preventMark){ // not rendered
7236             return;
7237         }
7238         this.el.addClass(this.invalidClass);
7239         /*
7240         msg = msg || this.invalidText;
7241         switch(this.msgTarget){
7242             case 'qtip':
7243                 this.el.dom.qtip = msg;
7244                 this.el.dom.qclass = 'x-form-invalid-tip';
7245                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
7246                     Roo.QuickTips.enable();
7247                 }
7248                 break;
7249             case 'title':
7250                 this.el.dom.title = msg;
7251                 break;
7252             case 'under':
7253                 if(!this.errorEl){
7254                     var elp = this.el.findParent('.x-form-element', 5, true);
7255                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
7256                     this.errorEl.setWidth(elp.getWidth(true)-20);
7257                 }
7258                 this.errorEl.update(msg);
7259                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
7260                 break;
7261             case 'side':
7262                 if(!this.errorIcon){
7263                     var elp = this.el.findParent('.x-form-element', 5, true);
7264                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
7265                 }
7266                 this.alignErrorIcon();
7267                 this.errorIcon.dom.qtip = msg;
7268                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
7269                 this.errorIcon.show();
7270                 this.on('resize', this.alignErrorIcon, this);
7271                 break;
7272             default:
7273                 var t = Roo.getDom(this.msgTarget);
7274                 t.innerHTML = msg;
7275                 t.style.display = this.msgDisplay;
7276                 break;
7277         }
7278         */
7279         this.fireEvent('invalid', this, msg);
7280     },
7281     // private
7282     SafariOnKeyDown : function(event)
7283     {
7284         // this is a workaround for a password hang bug on chrome/ webkit.
7285         
7286         var isSelectAll = false;
7287         
7288         if(this.inputEl().dom.selectionEnd > 0){
7289             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
7290         }
7291         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
7292             event.preventDefault();
7293             this.setValue('');
7294             return;
7295         }
7296         
7297         if(isSelectAll){ // backspace and delete key
7298             
7299             event.preventDefault();
7300             // this is very hacky as keydown always get's upper case.
7301             //
7302             var cc = String.fromCharCode(event.getCharCode());
7303             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
7304             
7305         }
7306     },
7307     adjustWidth : function(tag, w){
7308         tag = tag.toLowerCase();
7309         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
7310             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
7311                 if(tag == 'input'){
7312                     return w + 2;
7313                 }
7314                 if(tag == 'textarea'){
7315                     return w-2;
7316                 }
7317             }else if(Roo.isOpera){
7318                 if(tag == 'input'){
7319                     return w + 2;
7320                 }
7321                 if(tag == 'textarea'){
7322                     return w-2;
7323                 }
7324             }
7325         }
7326         return w;
7327     }
7328     
7329 });
7330
7331  
7332 /*
7333  * - LGPL
7334  *
7335  * Input
7336  * 
7337  */
7338
7339 /**
7340  * @class Roo.bootstrap.TextArea
7341  * @extends Roo.bootstrap.Input
7342  * Bootstrap TextArea class
7343  * @cfg {Number} cols Specifies the visible width of a text area
7344  * @cfg {Number} rows Specifies the visible number of lines in a text area
7345  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
7346  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
7347  * @cfg {string} html text
7348  * 
7349  * @constructor
7350  * Create a new TextArea
7351  * @param {Object} config The config object
7352  */
7353
7354 Roo.bootstrap.TextArea = function(config){
7355     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
7356    
7357 };
7358
7359 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
7360      
7361     cols : false,
7362     rows : 5,
7363     readOnly : false,
7364     warp : 'soft',
7365     resize : false,
7366     value: false,
7367     html: false,
7368     
7369     getAutoCreate : function(){
7370         
7371         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7372         
7373         var id = Roo.id();
7374         
7375         var cfg = {};
7376         
7377         var input =  {
7378             tag: 'textarea',
7379             id : id,
7380             warp : this.warp,
7381             rows : this.rows,
7382             value : this.value || '',
7383             html: this.html || '',
7384             cls : 'form-control',
7385             placeholder : this.placeholder || '' 
7386             
7387         };
7388         
7389         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7390             input.maxLength = this.maxLength;
7391         }
7392         
7393         if(this.resize){
7394             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
7395         }
7396         
7397         if(this.cols){
7398             input.cols = this.cols;
7399         }
7400         
7401         if (this.readOnly) {
7402             input.readonly = true;
7403         }
7404         
7405         if (this.name) {
7406             input.name = this.name;
7407         }
7408         
7409         if (this.size) {
7410             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
7411         }
7412         
7413         var settings=this;
7414         ['xs','sm','md','lg'].map(function(size){
7415             if (settings[size]) {
7416                 cfg.cls += ' col-' + size + '-' + settings[size];
7417             }
7418         });
7419         
7420         var inputblock = input;
7421         
7422         if (this.before || this.after) {
7423             
7424             inputblock = {
7425                 cls : 'input-group',
7426                 cn :  [] 
7427             };
7428             if (this.before) {
7429                 inputblock.cn.push({
7430                     tag :'span',
7431                     cls : 'input-group-addon',
7432                     html : this.before
7433                 });
7434             }
7435             inputblock.cn.push(input);
7436             if (this.after) {
7437                 inputblock.cn.push({
7438                     tag :'span',
7439                     cls : 'input-group-addon',
7440                     html : this.after
7441                 });
7442             }
7443             
7444         }
7445         
7446         if (align ==='left' && this.fieldLabel.length) {
7447                 Roo.log("left and has label");
7448                 cfg.cn = [
7449                     
7450                     {
7451                         tag: 'label',
7452                         'for' :  id,
7453                         cls : 'control-label col-sm-' + this.labelWidth,
7454                         html : this.fieldLabel
7455                         
7456                     },
7457                     {
7458                         cls : "col-sm-" + (12 - this.labelWidth), 
7459                         cn: [
7460                             inputblock
7461                         ]
7462                     }
7463                     
7464                 ];
7465         } else if ( this.fieldLabel.length) {
7466                 Roo.log(" label");
7467                  cfg.cn = [
7468                    
7469                     {
7470                         tag: 'label',
7471                         //cls : 'input-group-addon',
7472                         html : this.fieldLabel
7473                         
7474                     },
7475                     
7476                     inputblock
7477                     
7478                 ];
7479
7480         } else {
7481             
7482                    Roo.log(" no label && no align");
7483                 cfg.cn = [
7484                     
7485                         inputblock
7486                     
7487                 ];
7488                 
7489                 
7490         }
7491         
7492         if (this.disabled) {
7493             input.disabled=true;
7494         }
7495         
7496         return cfg;
7497         
7498     },
7499     /**
7500      * return the real textarea element.
7501      */
7502     inputEl: function ()
7503     {
7504         return this.el.select('textarea.form-control',true).first();
7505     }
7506 });
7507
7508  
7509 /*
7510  * - LGPL
7511  *
7512  * trigger field - base class for combo..
7513  * 
7514  */
7515  
7516 /**
7517  * @class Roo.bootstrap.TriggerField
7518  * @extends Roo.bootstrap.Input
7519  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
7520  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
7521  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
7522  * for which you can provide a custom implementation.  For example:
7523  * <pre><code>
7524 var trigger = new Roo.bootstrap.TriggerField();
7525 trigger.onTriggerClick = myTriggerFn;
7526 trigger.applyTo('my-field');
7527 </code></pre>
7528  *
7529  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
7530  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
7531  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
7532  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
7533  * @constructor
7534  * Create a new TriggerField.
7535  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
7536  * to the base TextField)
7537  */
7538 Roo.bootstrap.TriggerField = function(config){
7539     this.mimicing = false;
7540     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
7541 };
7542
7543 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
7544     /**
7545      * @cfg {String} triggerClass A CSS class to apply to the trigger
7546      */
7547      /**
7548      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
7549      */
7550     hideTrigger:false,
7551
7552     /** @cfg {Boolean} grow @hide */
7553     /** @cfg {Number} growMin @hide */
7554     /** @cfg {Number} growMax @hide */
7555
7556     /**
7557      * @hide 
7558      * @method
7559      */
7560     autoSize: Roo.emptyFn,
7561     // private
7562     monitorTab : true,
7563     // private
7564     deferHeight : true,
7565
7566     
7567     actionMode : 'wrap',
7568     
7569     
7570     
7571     getAutoCreate : function(){
7572        
7573         var parent = this.parent();
7574         
7575         var align = this.labelAlign || this.parentLabelAlign();
7576         
7577         var id = Roo.id();
7578         
7579         var cfg = {
7580             cls: 'form-group' //input-group
7581         };
7582         
7583         
7584         var input =  {
7585             tag: 'input',
7586             id : id,
7587             type : this.inputType,
7588             cls : 'form-control',
7589             autocomplete: 'off',
7590             placeholder : this.placeholder || '' 
7591             
7592         };
7593         if (this.name) {
7594             input.name = this.name;
7595         }
7596         if (this.size) {
7597             input.cls += ' input-' + this.size;
7598         }
7599         
7600         if (this.disabled) {
7601             input.disabled=true;
7602         }
7603         
7604         var inputblock = input;
7605         
7606         if (this.before || this.after) {
7607             
7608             inputblock = {
7609                 cls : 'input-group',
7610                 cn :  [] 
7611             };
7612             if (this.before) {
7613                 inputblock.cn.push({
7614                     tag :'span',
7615                     cls : 'input-group-addon',
7616                     html : this.before
7617                 });
7618             }
7619             inputblock.cn.push(input);
7620             if (this.after) {
7621                 inputblock.cn.push({
7622                     tag :'span',
7623                     cls : 'input-group-addon',
7624                     html : this.after
7625                 });
7626             }
7627             
7628         };
7629         
7630         var box = {
7631             tag: 'div',
7632             cn: [
7633                 {
7634                     tag: 'input',
7635                     type : 'hidden',
7636                     cls: 'form-hidden-field'
7637                 },
7638                 inputblock
7639             ]
7640             
7641         };
7642         
7643         if(this.multiple){
7644             Roo.log('multiple');
7645             
7646             box = {
7647                 tag: 'div',
7648                 cn: [
7649                     {
7650                         tag: 'input',
7651                         type : 'hidden',
7652                         cls: 'form-hidden-field'
7653                     },
7654                     {
7655                         tag: 'ul',
7656                         cls: 'select2-choices',
7657                         cn:[
7658                             {
7659                                 tag: 'li',
7660                                 cls: 'select2-search-field',
7661                                 cn: [
7662
7663                                     inputblock
7664                                 ]
7665                             }
7666                         ]
7667                     }
7668                 ]
7669             }
7670         };
7671         
7672         var combobox = {
7673             cls: 'select2-container input-group',
7674             cn: [
7675                 box,
7676                 {
7677                     tag: 'ul',
7678                     cls: 'typeahead typeahead-long dropdown-menu',
7679                     style: 'display:none'
7680                 }
7681             ]
7682         };
7683         
7684         if(!this.multiple){
7685             combobox.cn.push({
7686                 tag :'span',
7687                 cls : 'input-group-addon btn dropdown-toggle',
7688                 cn : [
7689                     {
7690                         tag: 'span',
7691                         cls: 'caret'
7692                     },
7693                     {
7694                         tag: 'span',
7695                         cls: 'combobox-clear',
7696                         cn  : [
7697                             {
7698                                 tag : 'i',
7699                                 cls: 'icon-remove'
7700                             }
7701                         ]
7702                     }
7703                 ]
7704
7705             })
7706         }
7707         
7708         if(this.multiple){
7709             combobox.cls += ' select2-container-multi';
7710         }
7711         
7712         if (align ==='left' && this.fieldLabel.length) {
7713             
7714                 Roo.log("left and has label");
7715                 cfg.cn = [
7716                     
7717                     {
7718                         tag: 'label',
7719                         'for' :  id,
7720                         cls : 'control-label col-sm-' + this.labelWidth,
7721                         html : this.fieldLabel
7722                         
7723                     },
7724                     {
7725                         cls : "col-sm-" + (12 - this.labelWidth), 
7726                         cn: [
7727                             combobox
7728                         ]
7729                     }
7730                     
7731                 ];
7732         } else if ( this.fieldLabel.length) {
7733                 Roo.log(" label");
7734                  cfg.cn = [
7735                    
7736                     {
7737                         tag: 'label',
7738                         //cls : 'input-group-addon',
7739                         html : this.fieldLabel
7740                         
7741                     },
7742                     
7743                     combobox
7744                     
7745                 ];
7746
7747         } else {
7748             
7749                 Roo.log(" no label && no align");
7750                 cfg = combobox
7751                      
7752                 
7753         }
7754          
7755         var settings=this;
7756         ['xs','sm','md','lg'].map(function(size){
7757             if (settings[size]) {
7758                 cfg.cls += ' col-' + size + '-' + settings[size];
7759             }
7760         });
7761         
7762         return cfg;
7763         
7764     },
7765     
7766     
7767     
7768     // private
7769     onResize : function(w, h){
7770 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
7771 //        if(typeof w == 'number'){
7772 //            var x = w - this.trigger.getWidth();
7773 //            this.inputEl().setWidth(this.adjustWidth('input', x));
7774 //            this.trigger.setStyle('left', x+'px');
7775 //        }
7776     },
7777
7778     // private
7779     adjustSize : Roo.BoxComponent.prototype.adjustSize,
7780
7781     // private
7782     getResizeEl : function(){
7783         return this.inputEl();
7784     },
7785
7786     // private
7787     getPositionEl : function(){
7788         return this.inputEl();
7789     },
7790
7791     // private
7792     alignErrorIcon : function(){
7793         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
7794     },
7795
7796     // private
7797     initEvents : function(){
7798         
7799         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
7800         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
7801         if(!this.multiple){
7802             this.trigger = this.el.select('span.dropdown-toggle',true).first();
7803             if(this.hideTrigger){
7804                 this.trigger.setDisplayed(false);
7805             }
7806             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
7807         }
7808         
7809         if(this.multiple){
7810             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
7811         }
7812         
7813         //this.trigger.addClassOnOver('x-form-trigger-over');
7814         //this.trigger.addClassOnClick('x-form-trigger-click');
7815         
7816         //if(!this.width){
7817         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
7818         //}
7819     },
7820
7821     // private
7822     initTrigger : function(){
7823        
7824     },
7825
7826     // private
7827     onDestroy : function(){
7828         if(this.trigger){
7829             this.trigger.removeAllListeners();
7830           //  this.trigger.remove();
7831         }
7832         //if(this.wrap){
7833         //    this.wrap.remove();
7834         //}
7835         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
7836     },
7837
7838     // private
7839     onFocus : function(){
7840         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
7841         /*
7842         if(!this.mimicing){
7843             this.wrap.addClass('x-trigger-wrap-focus');
7844             this.mimicing = true;
7845             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
7846             if(this.monitorTab){
7847                 this.el.on("keydown", this.checkTab, this);
7848             }
7849         }
7850         */
7851     },
7852
7853     // private
7854     checkTab : function(e){
7855         if(e.getKey() == e.TAB){
7856             this.triggerBlur();
7857         }
7858     },
7859
7860     // private
7861     onBlur : function(){
7862         // do nothing
7863     },
7864
7865     // private
7866     mimicBlur : function(e, t){
7867         /*
7868         if(!this.wrap.contains(t) && this.validateBlur()){
7869             this.triggerBlur();
7870         }
7871         */
7872     },
7873
7874     // private
7875     triggerBlur : function(){
7876         this.mimicing = false;
7877         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
7878         if(this.monitorTab){
7879             this.el.un("keydown", this.checkTab, this);
7880         }
7881         //this.wrap.removeClass('x-trigger-wrap-focus');
7882         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
7883     },
7884
7885     // private
7886     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
7887     validateBlur : function(e, t){
7888         return true;
7889     },
7890
7891     // private
7892     onDisable : function(){
7893         this.inputEl().dom.disabled = true;
7894         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
7895         //if(this.wrap){
7896         //    this.wrap.addClass('x-item-disabled');
7897         //}
7898     },
7899
7900     // private
7901     onEnable : function(){
7902         this.inputEl().dom.disabled = false;
7903         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
7904         //if(this.wrap){
7905         //    this.el.removeClass('x-item-disabled');
7906         //}
7907     },
7908
7909     // private
7910     onShow : function(){
7911         var ae = this.getActionEl();
7912         
7913         if(ae){
7914             ae.dom.style.display = '';
7915             ae.dom.style.visibility = 'visible';
7916         }
7917     },
7918
7919     // private
7920     
7921     onHide : function(){
7922         var ae = this.getActionEl();
7923         ae.dom.style.display = 'none';
7924     },
7925
7926     /**
7927      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
7928      * by an implementing function.
7929      * @method
7930      * @param {EventObject} e
7931      */
7932     onTriggerClick : Roo.emptyFn
7933 });
7934  /*
7935  * Based on:
7936  * Ext JS Library 1.1.1
7937  * Copyright(c) 2006-2007, Ext JS, LLC.
7938  *
7939  * Originally Released Under LGPL - original licence link has changed is not relivant.
7940  *
7941  * Fork - LGPL
7942  * <script type="text/javascript">
7943  */
7944
7945
7946 /**
7947  * @class Roo.data.SortTypes
7948  * @singleton
7949  * Defines the default sorting (casting?) comparison functions used when sorting data.
7950  */
7951 Roo.data.SortTypes = {
7952     /**
7953      * Default sort that does nothing
7954      * @param {Mixed} s The value being converted
7955      * @return {Mixed} The comparison value
7956      */
7957     none : function(s){
7958         return s;
7959     },
7960     
7961     /**
7962      * The regular expression used to strip tags
7963      * @type {RegExp}
7964      * @property
7965      */
7966     stripTagsRE : /<\/?[^>]+>/gi,
7967     
7968     /**
7969      * Strips all HTML tags to sort on text only
7970      * @param {Mixed} s The value being converted
7971      * @return {String} The comparison value
7972      */
7973     asText : function(s){
7974         return String(s).replace(this.stripTagsRE, "");
7975     },
7976     
7977     /**
7978      * Strips all HTML tags to sort on text only - Case insensitive
7979      * @param {Mixed} s The value being converted
7980      * @return {String} The comparison value
7981      */
7982     asUCText : function(s){
7983         return String(s).toUpperCase().replace(this.stripTagsRE, "");
7984     },
7985     
7986     /**
7987      * Case insensitive string
7988      * @param {Mixed} s The value being converted
7989      * @return {String} The comparison value
7990      */
7991     asUCString : function(s) {
7992         return String(s).toUpperCase();
7993     },
7994     
7995     /**
7996      * Date sorting
7997      * @param {Mixed} s The value being converted
7998      * @return {Number} The comparison value
7999      */
8000     asDate : function(s) {
8001         if(!s){
8002             return 0;
8003         }
8004         if(s instanceof Date){
8005             return s.getTime();
8006         }
8007         return Date.parse(String(s));
8008     },
8009     
8010     /**
8011      * Float sorting
8012      * @param {Mixed} s The value being converted
8013      * @return {Float} The comparison value
8014      */
8015     asFloat : function(s) {
8016         var val = parseFloat(String(s).replace(/,/g, ""));
8017         if(isNaN(val)) val = 0;
8018         return val;
8019     },
8020     
8021     /**
8022      * Integer sorting
8023      * @param {Mixed} s The value being converted
8024      * @return {Number} The comparison value
8025      */
8026     asInt : function(s) {
8027         var val = parseInt(String(s).replace(/,/g, ""));
8028         if(isNaN(val)) val = 0;
8029         return val;
8030     }
8031 };/*
8032  * Based on:
8033  * Ext JS Library 1.1.1
8034  * Copyright(c) 2006-2007, Ext JS, LLC.
8035  *
8036  * Originally Released Under LGPL - original licence link has changed is not relivant.
8037  *
8038  * Fork - LGPL
8039  * <script type="text/javascript">
8040  */
8041
8042 /**
8043 * @class Roo.data.Record
8044  * Instances of this class encapsulate both record <em>definition</em> information, and record
8045  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
8046  * to access Records cached in an {@link Roo.data.Store} object.<br>
8047  * <p>
8048  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
8049  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
8050  * objects.<br>
8051  * <p>
8052  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
8053  * @constructor
8054  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
8055  * {@link #create}. The parameters are the same.
8056  * @param {Array} data An associative Array of data values keyed by the field name.
8057  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
8058  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
8059  * not specified an integer id is generated.
8060  */
8061 Roo.data.Record = function(data, id){
8062     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
8063     this.data = data;
8064 };
8065
8066 /**
8067  * Generate a constructor for a specific record layout.
8068  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
8069  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
8070  * Each field definition object may contain the following properties: <ul>
8071  * <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,
8072  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
8073  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
8074  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
8075  * is being used, then this is a string containing the javascript expression to reference the data relative to 
8076  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
8077  * to the data item relative to the record element. If the mapping expression is the same as the field name,
8078  * this may be omitted.</p></li>
8079  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
8080  * <ul><li>auto (Default, implies no conversion)</li>
8081  * <li>string</li>
8082  * <li>int</li>
8083  * <li>float</li>
8084  * <li>boolean</li>
8085  * <li>date</li></ul></p></li>
8086  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
8087  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
8088  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
8089  * by the Reader into an object that will be stored in the Record. It is passed the
8090  * following parameters:<ul>
8091  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
8092  * </ul></p></li>
8093  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
8094  * </ul>
8095  * <br>usage:<br><pre><code>
8096 var TopicRecord = Roo.data.Record.create(
8097     {name: 'title', mapping: 'topic_title'},
8098     {name: 'author', mapping: 'username'},
8099     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
8100     {name: 'lastPost', mapping: 'post_time', type: 'date'},
8101     {name: 'lastPoster', mapping: 'user2'},
8102     {name: 'excerpt', mapping: 'post_text'}
8103 );
8104
8105 var myNewRecord = new TopicRecord({
8106     title: 'Do my job please',
8107     author: 'noobie',
8108     totalPosts: 1,
8109     lastPost: new Date(),
8110     lastPoster: 'Animal',
8111     excerpt: 'No way dude!'
8112 });
8113 myStore.add(myNewRecord);
8114 </code></pre>
8115  * @method create
8116  * @static
8117  */
8118 Roo.data.Record.create = function(o){
8119     var f = function(){
8120         f.superclass.constructor.apply(this, arguments);
8121     };
8122     Roo.extend(f, Roo.data.Record);
8123     var p = f.prototype;
8124     p.fields = new Roo.util.MixedCollection(false, function(field){
8125         return field.name;
8126     });
8127     for(var i = 0, len = o.length; i < len; i++){
8128         p.fields.add(new Roo.data.Field(o[i]));
8129     }
8130     f.getField = function(name){
8131         return p.fields.get(name);  
8132     };
8133     return f;
8134 };
8135
8136 Roo.data.Record.AUTO_ID = 1000;
8137 Roo.data.Record.EDIT = 'edit';
8138 Roo.data.Record.REJECT = 'reject';
8139 Roo.data.Record.COMMIT = 'commit';
8140
8141 Roo.data.Record.prototype = {
8142     /**
8143      * Readonly flag - true if this record has been modified.
8144      * @type Boolean
8145      */
8146     dirty : false,
8147     editing : false,
8148     error: null,
8149     modified: null,
8150
8151     // private
8152     join : function(store){
8153         this.store = store;
8154     },
8155
8156     /**
8157      * Set the named field to the specified value.
8158      * @param {String} name The name of the field to set.
8159      * @param {Object} value The value to set the field to.
8160      */
8161     set : function(name, value){
8162         if(this.data[name] == value){
8163             return;
8164         }
8165         this.dirty = true;
8166         if(!this.modified){
8167             this.modified = {};
8168         }
8169         if(typeof this.modified[name] == 'undefined'){
8170             this.modified[name] = this.data[name];
8171         }
8172         this.data[name] = value;
8173         if(!this.editing && this.store){
8174             this.store.afterEdit(this);
8175         }       
8176     },
8177
8178     /**
8179      * Get the value of the named field.
8180      * @param {String} name The name of the field to get the value of.
8181      * @return {Object} The value of the field.
8182      */
8183     get : function(name){
8184         return this.data[name]; 
8185     },
8186
8187     // private
8188     beginEdit : function(){
8189         this.editing = true;
8190         this.modified = {}; 
8191     },
8192
8193     // private
8194     cancelEdit : function(){
8195         this.editing = false;
8196         delete this.modified;
8197     },
8198
8199     // private
8200     endEdit : function(){
8201         this.editing = false;
8202         if(this.dirty && this.store){
8203             this.store.afterEdit(this);
8204         }
8205     },
8206
8207     /**
8208      * Usually called by the {@link Roo.data.Store} which owns the Record.
8209      * Rejects all changes made to the Record since either creation, or the last commit operation.
8210      * Modified fields are reverted to their original values.
8211      * <p>
8212      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8213      * of reject operations.
8214      */
8215     reject : function(){
8216         var m = this.modified;
8217         for(var n in m){
8218             if(typeof m[n] != "function"){
8219                 this.data[n] = m[n];
8220             }
8221         }
8222         this.dirty = false;
8223         delete this.modified;
8224         this.editing = false;
8225         if(this.store){
8226             this.store.afterReject(this);
8227         }
8228     },
8229
8230     /**
8231      * Usually called by the {@link Roo.data.Store} which owns the Record.
8232      * Commits all changes made to the Record since either creation, or the last commit operation.
8233      * <p>
8234      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8235      * of commit operations.
8236      */
8237     commit : function(){
8238         this.dirty = false;
8239         delete this.modified;
8240         this.editing = false;
8241         if(this.store){
8242             this.store.afterCommit(this);
8243         }
8244     },
8245
8246     // private
8247     hasError : function(){
8248         return this.error != null;
8249     },
8250
8251     // private
8252     clearError : function(){
8253         this.error = null;
8254     },
8255
8256     /**
8257      * Creates a copy of this record.
8258      * @param {String} id (optional) A new record id if you don't want to use this record's id
8259      * @return {Record}
8260      */
8261     copy : function(newId) {
8262         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
8263     }
8264 };/*
8265  * Based on:
8266  * Ext JS Library 1.1.1
8267  * Copyright(c) 2006-2007, Ext JS, LLC.
8268  *
8269  * Originally Released Under LGPL - original licence link has changed is not relivant.
8270  *
8271  * Fork - LGPL
8272  * <script type="text/javascript">
8273  */
8274
8275
8276
8277 /**
8278  * @class Roo.data.Store
8279  * @extends Roo.util.Observable
8280  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
8281  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
8282  * <p>
8283  * 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
8284  * has no knowledge of the format of the data returned by the Proxy.<br>
8285  * <p>
8286  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
8287  * instances from the data object. These records are cached and made available through accessor functions.
8288  * @constructor
8289  * Creates a new Store.
8290  * @param {Object} config A config object containing the objects needed for the Store to access data,
8291  * and read the data into Records.
8292  */
8293 Roo.data.Store = function(config){
8294     this.data = new Roo.util.MixedCollection(false);
8295     this.data.getKey = function(o){
8296         return o.id;
8297     };
8298     this.baseParams = {};
8299     // private
8300     this.paramNames = {
8301         "start" : "start",
8302         "limit" : "limit",
8303         "sort" : "sort",
8304         "dir" : "dir",
8305         "multisort" : "_multisort"
8306     };
8307
8308     if(config && config.data){
8309         this.inlineData = config.data;
8310         delete config.data;
8311     }
8312
8313     Roo.apply(this, config);
8314     
8315     if(this.reader){ // reader passed
8316         this.reader = Roo.factory(this.reader, Roo.data);
8317         this.reader.xmodule = this.xmodule || false;
8318         if(!this.recordType){
8319             this.recordType = this.reader.recordType;
8320         }
8321         if(this.reader.onMetaChange){
8322             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
8323         }
8324     }
8325
8326     if(this.recordType){
8327         this.fields = this.recordType.prototype.fields;
8328     }
8329     this.modified = [];
8330
8331     this.addEvents({
8332         /**
8333          * @event datachanged
8334          * Fires when the data cache has changed, and a widget which is using this Store
8335          * as a Record cache should refresh its view.
8336          * @param {Store} this
8337          */
8338         datachanged : true,
8339         /**
8340          * @event metachange
8341          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
8342          * @param {Store} this
8343          * @param {Object} meta The JSON metadata
8344          */
8345         metachange : true,
8346         /**
8347          * @event add
8348          * Fires when Records have been added to the Store
8349          * @param {Store} this
8350          * @param {Roo.data.Record[]} records The array of Records added
8351          * @param {Number} index The index at which the record(s) were added
8352          */
8353         add : true,
8354         /**
8355          * @event remove
8356          * Fires when a Record has been removed from the Store
8357          * @param {Store} this
8358          * @param {Roo.data.Record} record The Record that was removed
8359          * @param {Number} index The index at which the record was removed
8360          */
8361         remove : true,
8362         /**
8363          * @event update
8364          * Fires when a Record has been updated
8365          * @param {Store} this
8366          * @param {Roo.data.Record} record The Record that was updated
8367          * @param {String} operation The update operation being performed.  Value may be one of:
8368          * <pre><code>
8369  Roo.data.Record.EDIT
8370  Roo.data.Record.REJECT
8371  Roo.data.Record.COMMIT
8372          * </code></pre>
8373          */
8374         update : true,
8375         /**
8376          * @event clear
8377          * Fires when the data cache has been cleared.
8378          * @param {Store} this
8379          */
8380         clear : true,
8381         /**
8382          * @event beforeload
8383          * Fires before a request is made for a new data object.  If the beforeload handler returns false
8384          * the load action will be canceled.
8385          * @param {Store} this
8386          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8387          */
8388         beforeload : true,
8389         /**
8390          * @event beforeloadadd
8391          * Fires after a new set of Records has been loaded.
8392          * @param {Store} this
8393          * @param {Roo.data.Record[]} records The Records that were loaded
8394          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8395          */
8396         beforeloadadd : true,
8397         /**
8398          * @event load
8399          * Fires after a new set of Records has been loaded, before they are added to the store.
8400          * @param {Store} this
8401          * @param {Roo.data.Record[]} records The Records that were loaded
8402          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8403          * @params {Object} return from reader
8404          */
8405         load : true,
8406         /**
8407          * @event loadexception
8408          * Fires if an exception occurs in the Proxy during loading.
8409          * Called with the signature of the Proxy's "loadexception" event.
8410          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
8411          * 
8412          * @param {Proxy} 
8413          * @param {Object} return from JsonData.reader() - success, totalRecords, records
8414          * @param {Object} load options 
8415          * @param {Object} jsonData from your request (normally this contains the Exception)
8416          */
8417         loadexception : true
8418     });
8419     
8420     if(this.proxy){
8421         this.proxy = Roo.factory(this.proxy, Roo.data);
8422         this.proxy.xmodule = this.xmodule || false;
8423         this.relayEvents(this.proxy,  ["loadexception"]);
8424     }
8425     this.sortToggle = {};
8426     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
8427
8428     Roo.data.Store.superclass.constructor.call(this);
8429
8430     if(this.inlineData){
8431         this.loadData(this.inlineData);
8432         delete this.inlineData;
8433     }
8434 };
8435
8436 Roo.extend(Roo.data.Store, Roo.util.Observable, {
8437      /**
8438     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
8439     * without a remote query - used by combo/forms at present.
8440     */
8441     
8442     /**
8443     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
8444     */
8445     /**
8446     * @cfg {Array} data Inline data to be loaded when the store is initialized.
8447     */
8448     /**
8449     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
8450     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
8451     */
8452     /**
8453     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
8454     * on any HTTP request
8455     */
8456     /**
8457     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
8458     */
8459     /**
8460     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
8461     */
8462     multiSort: false,
8463     /**
8464     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
8465     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
8466     */
8467     remoteSort : false,
8468
8469     /**
8470     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
8471      * loaded or when a record is removed. (defaults to false).
8472     */
8473     pruneModifiedRecords : false,
8474
8475     // private
8476     lastOptions : null,
8477
8478     /**
8479      * Add Records to the Store and fires the add event.
8480      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8481      */
8482     add : function(records){
8483         records = [].concat(records);
8484         for(var i = 0, len = records.length; i < len; i++){
8485             records[i].join(this);
8486         }
8487         var index = this.data.length;
8488         this.data.addAll(records);
8489         this.fireEvent("add", this, records, index);
8490     },
8491
8492     /**
8493      * Remove a Record from the Store and fires the remove event.
8494      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
8495      */
8496     remove : function(record){
8497         var index = this.data.indexOf(record);
8498         this.data.removeAt(index);
8499         if(this.pruneModifiedRecords){
8500             this.modified.remove(record);
8501         }
8502         this.fireEvent("remove", this, record, index);
8503     },
8504
8505     /**
8506      * Remove all Records from the Store and fires the clear event.
8507      */
8508     removeAll : function(){
8509         this.data.clear();
8510         if(this.pruneModifiedRecords){
8511             this.modified = [];
8512         }
8513         this.fireEvent("clear", this);
8514     },
8515
8516     /**
8517      * Inserts Records to the Store at the given index and fires the add event.
8518      * @param {Number} index The start index at which to insert the passed Records.
8519      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8520      */
8521     insert : function(index, records){
8522         records = [].concat(records);
8523         for(var i = 0, len = records.length; i < len; i++){
8524             this.data.insert(index, records[i]);
8525             records[i].join(this);
8526         }
8527         this.fireEvent("add", this, records, index);
8528     },
8529
8530     /**
8531      * Get the index within the cache of the passed Record.
8532      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
8533      * @return {Number} The index of the passed Record. Returns -1 if not found.
8534      */
8535     indexOf : function(record){
8536         return this.data.indexOf(record);
8537     },
8538
8539     /**
8540      * Get the index within the cache of the Record with the passed id.
8541      * @param {String} id The id of the Record to find.
8542      * @return {Number} The index of the Record. Returns -1 if not found.
8543      */
8544     indexOfId : function(id){
8545         return this.data.indexOfKey(id);
8546     },
8547
8548     /**
8549      * Get the Record with the specified id.
8550      * @param {String} id The id of the Record to find.
8551      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
8552      */
8553     getById : function(id){
8554         return this.data.key(id);
8555     },
8556
8557     /**
8558      * Get the Record at the specified index.
8559      * @param {Number} index The index of the Record to find.
8560      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
8561      */
8562     getAt : function(index){
8563         return this.data.itemAt(index);
8564     },
8565
8566     /**
8567      * Returns a range of Records between specified indices.
8568      * @param {Number} startIndex (optional) The starting index (defaults to 0)
8569      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
8570      * @return {Roo.data.Record[]} An array of Records
8571      */
8572     getRange : function(start, end){
8573         return this.data.getRange(start, end);
8574     },
8575
8576     // private
8577     storeOptions : function(o){
8578         o = Roo.apply({}, o);
8579         delete o.callback;
8580         delete o.scope;
8581         this.lastOptions = o;
8582     },
8583
8584     /**
8585      * Loads the Record cache from the configured Proxy using the configured Reader.
8586      * <p>
8587      * If using remote paging, then the first load call must specify the <em>start</em>
8588      * and <em>limit</em> properties in the options.params property to establish the initial
8589      * position within the dataset, and the number of Records to cache on each read from the Proxy.
8590      * <p>
8591      * <strong>It is important to note that for remote data sources, loading is asynchronous,
8592      * and this call will return before the new data has been loaded. Perform any post-processing
8593      * in a callback function, or in a "load" event handler.</strong>
8594      * <p>
8595      * @param {Object} options An object containing properties which control loading options:<ul>
8596      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
8597      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
8598      * passed the following arguments:<ul>
8599      * <li>r : Roo.data.Record[]</li>
8600      * <li>options: Options object from the load call</li>
8601      * <li>success: Boolean success indicator</li></ul></li>
8602      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
8603      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
8604      * </ul>
8605      */
8606     load : function(options){
8607         options = options || {};
8608         if(this.fireEvent("beforeload", this, options) !== false){
8609             this.storeOptions(options);
8610             var p = Roo.apply(options.params || {}, this.baseParams);
8611             // if meta was not loaded from remote source.. try requesting it.
8612             if (!this.reader.metaFromRemote) {
8613                 p._requestMeta = 1;
8614             }
8615             if(this.sortInfo && this.remoteSort){
8616                 var pn = this.paramNames;
8617                 p[pn["sort"]] = this.sortInfo.field;
8618                 p[pn["dir"]] = this.sortInfo.direction;
8619             }
8620             if (this.multiSort) {
8621                 var pn = this.paramNames;
8622                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
8623             }
8624             
8625             this.proxy.load(p, this.reader, this.loadRecords, this, options);
8626         }
8627     },
8628
8629     /**
8630      * Reloads the Record cache from the configured Proxy using the configured Reader and
8631      * the options from the last load operation performed.
8632      * @param {Object} options (optional) An object containing properties which may override the options
8633      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
8634      * the most recently used options are reused).
8635      */
8636     reload : function(options){
8637         this.load(Roo.applyIf(options||{}, this.lastOptions));
8638     },
8639
8640     // private
8641     // Called as a callback by the Reader during a load operation.
8642     loadRecords : function(o, options, success){
8643         if(!o || success === false){
8644             if(success !== false){
8645                 this.fireEvent("load", this, [], options, o);
8646             }
8647             if(options.callback){
8648                 options.callback.call(options.scope || this, [], options, false);
8649             }
8650             return;
8651         }
8652         // if data returned failure - throw an exception.
8653         if (o.success === false) {
8654             // show a message if no listener is registered.
8655             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
8656                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
8657             }
8658             // loadmask wil be hooked into this..
8659             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
8660             return;
8661         }
8662         var r = o.records, t = o.totalRecords || r.length;
8663         
8664         this.fireEvent("beforeloadadd", this, r, options, o);
8665         
8666         if(!options || options.add !== true){
8667             if(this.pruneModifiedRecords){
8668                 this.modified = [];
8669             }
8670             for(var i = 0, len = r.length; i < len; i++){
8671                 r[i].join(this);
8672             }
8673             if(this.snapshot){
8674                 this.data = this.snapshot;
8675                 delete this.snapshot;
8676             }
8677             this.data.clear();
8678             this.data.addAll(r);
8679             this.totalLength = t;
8680             this.applySort();
8681             this.fireEvent("datachanged", this);
8682         }else{
8683             this.totalLength = Math.max(t, this.data.length+r.length);
8684             this.add(r);
8685         }
8686         this.fireEvent("load", this, r, options, o);
8687         if(options.callback){
8688             options.callback.call(options.scope || this, r, options, true);
8689         }
8690     },
8691
8692
8693     /**
8694      * Loads data from a passed data block. A Reader which understands the format of the data
8695      * must have been configured in the constructor.
8696      * @param {Object} data The data block from which to read the Records.  The format of the data expected
8697      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
8698      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
8699      */
8700     loadData : function(o, append){
8701         var r = this.reader.readRecords(o);
8702         this.loadRecords(r, {add: append}, true);
8703     },
8704
8705     /**
8706      * Gets the number of cached records.
8707      * <p>
8708      * <em>If using paging, this may not be the total size of the dataset. If the data object
8709      * used by the Reader contains the dataset size, then the getTotalCount() function returns
8710      * the data set size</em>
8711      */
8712     getCount : function(){
8713         return this.data.length || 0;
8714     },
8715
8716     /**
8717      * Gets the total number of records in the dataset as returned by the server.
8718      * <p>
8719      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
8720      * the dataset size</em>
8721      */
8722     getTotalCount : function(){
8723         return this.totalLength || 0;
8724     },
8725
8726     /**
8727      * Returns the sort state of the Store as an object with two properties:
8728      * <pre><code>
8729  field {String} The name of the field by which the Records are sorted
8730  direction {String} The sort order, "ASC" or "DESC"
8731      * </code></pre>
8732      */
8733     getSortState : function(){
8734         return this.sortInfo;
8735     },
8736
8737     // private
8738     applySort : function(){
8739         if(this.sortInfo && !this.remoteSort){
8740             var s = this.sortInfo, f = s.field;
8741             var st = this.fields.get(f).sortType;
8742             var fn = function(r1, r2){
8743                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
8744                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
8745             };
8746             this.data.sort(s.direction, fn);
8747             if(this.snapshot && this.snapshot != this.data){
8748                 this.snapshot.sort(s.direction, fn);
8749             }
8750         }
8751     },
8752
8753     /**
8754      * Sets the default sort column and order to be used by the next load operation.
8755      * @param {String} fieldName The name of the field to sort by.
8756      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
8757      */
8758     setDefaultSort : function(field, dir){
8759         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
8760     },
8761
8762     /**
8763      * Sort the Records.
8764      * If remote sorting is used, the sort is performed on the server, and the cache is
8765      * reloaded. If local sorting is used, the cache is sorted internally.
8766      * @param {String} fieldName The name of the field to sort by.
8767      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
8768      */
8769     sort : function(fieldName, dir){
8770         var f = this.fields.get(fieldName);
8771         if(!dir){
8772             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
8773             
8774             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
8775                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
8776             }else{
8777                 dir = f.sortDir;
8778             }
8779         }
8780         this.sortToggle[f.name] = dir;
8781         this.sortInfo = {field: f.name, direction: dir};
8782         if(!this.remoteSort){
8783             this.applySort();
8784             this.fireEvent("datachanged", this);
8785         }else{
8786             this.load(this.lastOptions);
8787         }
8788     },
8789
8790     /**
8791      * Calls the specified function for each of the Records in the cache.
8792      * @param {Function} fn The function to call. The Record is passed as the first parameter.
8793      * Returning <em>false</em> aborts and exits the iteration.
8794      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
8795      */
8796     each : function(fn, scope){
8797         this.data.each(fn, scope);
8798     },
8799
8800     /**
8801      * Gets all records modified since the last commit.  Modified records are persisted across load operations
8802      * (e.g., during paging).
8803      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
8804      */
8805     getModifiedRecords : function(){
8806         return this.modified;
8807     },
8808
8809     // private
8810     createFilterFn : function(property, value, anyMatch){
8811         if(!value.exec){ // not a regex
8812             value = String(value);
8813             if(value.length == 0){
8814                 return false;
8815             }
8816             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
8817         }
8818         return function(r){
8819             return value.test(r.data[property]);
8820         };
8821     },
8822
8823     /**
8824      * Sums the value of <i>property</i> for each record between start and end and returns the result.
8825      * @param {String} property A field on your records
8826      * @param {Number} start The record index to start at (defaults to 0)
8827      * @param {Number} end The last record index to include (defaults to length - 1)
8828      * @return {Number} The sum
8829      */
8830     sum : function(property, start, end){
8831         var rs = this.data.items, v = 0;
8832         start = start || 0;
8833         end = (end || end === 0) ? end : rs.length-1;
8834
8835         for(var i = start; i <= end; i++){
8836             v += (rs[i].data[property] || 0);
8837         }
8838         return v;
8839     },
8840
8841     /**
8842      * Filter the records by a specified property.
8843      * @param {String} field A field on your records
8844      * @param {String/RegExp} value Either a string that the field
8845      * should start with or a RegExp to test against the field
8846      * @param {Boolean} anyMatch True to match any part not just the beginning
8847      */
8848     filter : function(property, value, anyMatch){
8849         var fn = this.createFilterFn(property, value, anyMatch);
8850         return fn ? this.filterBy(fn) : this.clearFilter();
8851     },
8852
8853     /**
8854      * Filter by a function. The specified function will be called with each
8855      * record in this data source. If the function returns true the record is included,
8856      * otherwise it is filtered.
8857      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
8858      * @param {Object} scope (optional) The scope of the function (defaults to this)
8859      */
8860     filterBy : function(fn, scope){
8861         this.snapshot = this.snapshot || this.data;
8862         this.data = this.queryBy(fn, scope||this);
8863         this.fireEvent("datachanged", this);
8864     },
8865
8866     /**
8867      * Query the records by a specified property.
8868      * @param {String} field A field on your records
8869      * @param {String/RegExp} value Either a string that the field
8870      * should start with or a RegExp to test against the field
8871      * @param {Boolean} anyMatch True to match any part not just the beginning
8872      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
8873      */
8874     query : function(property, value, anyMatch){
8875         var fn = this.createFilterFn(property, value, anyMatch);
8876         return fn ? this.queryBy(fn) : this.data.clone();
8877     },
8878
8879     /**
8880      * Query by a function. The specified function will be called with each
8881      * record in this data source. If the function returns true the record is included
8882      * in the results.
8883      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
8884      * @param {Object} scope (optional) The scope of the function (defaults to this)
8885       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
8886      **/
8887     queryBy : function(fn, scope){
8888         var data = this.snapshot || this.data;
8889         return data.filterBy(fn, scope||this);
8890     },
8891
8892     /**
8893      * Collects unique values for a particular dataIndex from this store.
8894      * @param {String} dataIndex The property to collect
8895      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
8896      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
8897      * @return {Array} An array of the unique values
8898      **/
8899     collect : function(dataIndex, allowNull, bypassFilter){
8900         var d = (bypassFilter === true && this.snapshot) ?
8901                 this.snapshot.items : this.data.items;
8902         var v, sv, r = [], l = {};
8903         for(var i = 0, len = d.length; i < len; i++){
8904             v = d[i].data[dataIndex];
8905             sv = String(v);
8906             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
8907                 l[sv] = true;
8908                 r[r.length] = v;
8909             }
8910         }
8911         return r;
8912     },
8913
8914     /**
8915      * Revert to a view of the Record cache with no filtering applied.
8916      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
8917      */
8918     clearFilter : function(suppressEvent){
8919         if(this.snapshot && this.snapshot != this.data){
8920             this.data = this.snapshot;
8921             delete this.snapshot;
8922             if(suppressEvent !== true){
8923                 this.fireEvent("datachanged", this);
8924             }
8925         }
8926     },
8927
8928     // private
8929     afterEdit : function(record){
8930         if(this.modified.indexOf(record) == -1){
8931             this.modified.push(record);
8932         }
8933         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
8934     },
8935     
8936     // private
8937     afterReject : function(record){
8938         this.modified.remove(record);
8939         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
8940     },
8941
8942     // private
8943     afterCommit : function(record){
8944         this.modified.remove(record);
8945         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
8946     },
8947
8948     /**
8949      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
8950      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
8951      */
8952     commitChanges : function(){
8953         var m = this.modified.slice(0);
8954         this.modified = [];
8955         for(var i = 0, len = m.length; i < len; i++){
8956             m[i].commit();
8957         }
8958     },
8959
8960     /**
8961      * Cancel outstanding changes on all changed records.
8962      */
8963     rejectChanges : function(){
8964         var m = this.modified.slice(0);
8965         this.modified = [];
8966         for(var i = 0, len = m.length; i < len; i++){
8967             m[i].reject();
8968         }
8969     },
8970
8971     onMetaChange : function(meta, rtype, o){
8972         this.recordType = rtype;
8973         this.fields = rtype.prototype.fields;
8974         delete this.snapshot;
8975         this.sortInfo = meta.sortInfo || this.sortInfo;
8976         this.modified = [];
8977         this.fireEvent('metachange', this, this.reader.meta);
8978     },
8979     
8980     moveIndex : function(data, type)
8981     {
8982         var index = this.indexOf(data);
8983         
8984         var newIndex = index + type;
8985         
8986         this.remove(data);
8987         
8988         this.insert(newIndex, data);
8989         
8990     }
8991 });/*
8992  * Based on:
8993  * Ext JS Library 1.1.1
8994  * Copyright(c) 2006-2007, Ext JS, LLC.
8995  *
8996  * Originally Released Under LGPL - original licence link has changed is not relivant.
8997  *
8998  * Fork - LGPL
8999  * <script type="text/javascript">
9000  */
9001
9002 /**
9003  * @class Roo.data.SimpleStore
9004  * @extends Roo.data.Store
9005  * Small helper class to make creating Stores from Array data easier.
9006  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
9007  * @cfg {Array} fields An array of field definition objects, or field name strings.
9008  * @cfg {Array} data The multi-dimensional array of data
9009  * @constructor
9010  * @param {Object} config
9011  */
9012 Roo.data.SimpleStore = function(config){
9013     Roo.data.SimpleStore.superclass.constructor.call(this, {
9014         isLocal : true,
9015         reader: new Roo.data.ArrayReader({
9016                 id: config.id
9017             },
9018             Roo.data.Record.create(config.fields)
9019         ),
9020         proxy : new Roo.data.MemoryProxy(config.data)
9021     });
9022     this.load();
9023 };
9024 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
9025  * Based on:
9026  * Ext JS Library 1.1.1
9027  * Copyright(c) 2006-2007, Ext JS, LLC.
9028  *
9029  * Originally Released Under LGPL - original licence link has changed is not relivant.
9030  *
9031  * Fork - LGPL
9032  * <script type="text/javascript">
9033  */
9034
9035 /**
9036 /**
9037  * @extends Roo.data.Store
9038  * @class Roo.data.JsonStore
9039  * Small helper class to make creating Stores for JSON data easier. <br/>
9040 <pre><code>
9041 var store = new Roo.data.JsonStore({
9042     url: 'get-images.php',
9043     root: 'images',
9044     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
9045 });
9046 </code></pre>
9047  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
9048  * JsonReader and HttpProxy (unless inline data is provided).</b>
9049  * @cfg {Array} fields An array of field definition objects, or field name strings.
9050  * @constructor
9051  * @param {Object} config
9052  */
9053 Roo.data.JsonStore = function(c){
9054     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
9055         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
9056         reader: new Roo.data.JsonReader(c, c.fields)
9057     }));
9058 };
9059 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
9060  * Based on:
9061  * Ext JS Library 1.1.1
9062  * Copyright(c) 2006-2007, Ext JS, LLC.
9063  *
9064  * Originally Released Under LGPL - original licence link has changed is not relivant.
9065  *
9066  * Fork - LGPL
9067  * <script type="text/javascript">
9068  */
9069
9070  
9071 Roo.data.Field = function(config){
9072     if(typeof config == "string"){
9073         config = {name: config};
9074     }
9075     Roo.apply(this, config);
9076     
9077     if(!this.type){
9078         this.type = "auto";
9079     }
9080     
9081     var st = Roo.data.SortTypes;
9082     // named sortTypes are supported, here we look them up
9083     if(typeof this.sortType == "string"){
9084         this.sortType = st[this.sortType];
9085     }
9086     
9087     // set default sortType for strings and dates
9088     if(!this.sortType){
9089         switch(this.type){
9090             case "string":
9091                 this.sortType = st.asUCString;
9092                 break;
9093             case "date":
9094                 this.sortType = st.asDate;
9095                 break;
9096             default:
9097                 this.sortType = st.none;
9098         }
9099     }
9100
9101     // define once
9102     var stripRe = /[\$,%]/g;
9103
9104     // prebuilt conversion function for this field, instead of
9105     // switching every time we're reading a value
9106     if(!this.convert){
9107         var cv, dateFormat = this.dateFormat;
9108         switch(this.type){
9109             case "":
9110             case "auto":
9111             case undefined:
9112                 cv = function(v){ return v; };
9113                 break;
9114             case "string":
9115                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
9116                 break;
9117             case "int":
9118                 cv = function(v){
9119                     return v !== undefined && v !== null && v !== '' ?
9120                            parseInt(String(v).replace(stripRe, ""), 10) : '';
9121                     };
9122                 break;
9123             case "float":
9124                 cv = function(v){
9125                     return v !== undefined && v !== null && v !== '' ?
9126                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
9127                     };
9128                 break;
9129             case "bool":
9130             case "boolean":
9131                 cv = function(v){ return v === true || v === "true" || v == 1; };
9132                 break;
9133             case "date":
9134                 cv = function(v){
9135                     if(!v){
9136                         return '';
9137                     }
9138                     if(v instanceof Date){
9139                         return v;
9140                     }
9141                     if(dateFormat){
9142                         if(dateFormat == "timestamp"){
9143                             return new Date(v*1000);
9144                         }
9145                         return Date.parseDate(v, dateFormat);
9146                     }
9147                     var parsed = Date.parse(v);
9148                     return parsed ? new Date(parsed) : null;
9149                 };
9150              break;
9151             
9152         }
9153         this.convert = cv;
9154     }
9155 };
9156
9157 Roo.data.Field.prototype = {
9158     dateFormat: null,
9159     defaultValue: "",
9160     mapping: null,
9161     sortType : null,
9162     sortDir : "ASC"
9163 };/*
9164  * Based on:
9165  * Ext JS Library 1.1.1
9166  * Copyright(c) 2006-2007, Ext JS, LLC.
9167  *
9168  * Originally Released Under LGPL - original licence link has changed is not relivant.
9169  *
9170  * Fork - LGPL
9171  * <script type="text/javascript">
9172  */
9173  
9174 // Base class for reading structured data from a data source.  This class is intended to be
9175 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
9176
9177 /**
9178  * @class Roo.data.DataReader
9179  * Base class for reading structured data from a data source.  This class is intended to be
9180  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
9181  */
9182
9183 Roo.data.DataReader = function(meta, recordType){
9184     
9185     this.meta = meta;
9186     
9187     this.recordType = recordType instanceof Array ? 
9188         Roo.data.Record.create(recordType) : recordType;
9189 };
9190
9191 Roo.data.DataReader.prototype = {
9192      /**
9193      * Create an empty record
9194      * @param {Object} data (optional) - overlay some values
9195      * @return {Roo.data.Record} record created.
9196      */
9197     newRow :  function(d) {
9198         var da =  {};
9199         this.recordType.prototype.fields.each(function(c) {
9200             switch( c.type) {
9201                 case 'int' : da[c.name] = 0; break;
9202                 case 'date' : da[c.name] = new Date(); break;
9203                 case 'float' : da[c.name] = 0.0; break;
9204                 case 'boolean' : da[c.name] = false; break;
9205                 default : da[c.name] = ""; break;
9206             }
9207             
9208         });
9209         return new this.recordType(Roo.apply(da, d));
9210     }
9211     
9212 };/*
9213  * Based on:
9214  * Ext JS Library 1.1.1
9215  * Copyright(c) 2006-2007, Ext JS, LLC.
9216  *
9217  * Originally Released Under LGPL - original licence link has changed is not relivant.
9218  *
9219  * Fork - LGPL
9220  * <script type="text/javascript">
9221  */
9222
9223 /**
9224  * @class Roo.data.DataProxy
9225  * @extends Roo.data.Observable
9226  * This class is an abstract base class for implementations which provide retrieval of
9227  * unformatted data objects.<br>
9228  * <p>
9229  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
9230  * (of the appropriate type which knows how to parse the data object) to provide a block of
9231  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
9232  * <p>
9233  * Custom implementations must implement the load method as described in
9234  * {@link Roo.data.HttpProxy#load}.
9235  */
9236 Roo.data.DataProxy = function(){
9237     this.addEvents({
9238         /**
9239          * @event beforeload
9240          * Fires before a network request is made to retrieve a data object.
9241          * @param {Object} This DataProxy object.
9242          * @param {Object} params The params parameter to the load function.
9243          */
9244         beforeload : true,
9245         /**
9246          * @event load
9247          * Fires before the load method's callback is called.
9248          * @param {Object} This DataProxy object.
9249          * @param {Object} o The data object.
9250          * @param {Object} arg The callback argument object passed to the load function.
9251          */
9252         load : true,
9253         /**
9254          * @event loadexception
9255          * Fires if an Exception occurs during data retrieval.
9256          * @param {Object} This DataProxy object.
9257          * @param {Object} o The data object.
9258          * @param {Object} arg The callback argument object passed to the load function.
9259          * @param {Object} e The Exception.
9260          */
9261         loadexception : true
9262     });
9263     Roo.data.DataProxy.superclass.constructor.call(this);
9264 };
9265
9266 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
9267
9268     /**
9269      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
9270      */
9271 /*
9272  * Based on:
9273  * Ext JS Library 1.1.1
9274  * Copyright(c) 2006-2007, Ext JS, LLC.
9275  *
9276  * Originally Released Under LGPL - original licence link has changed is not relivant.
9277  *
9278  * Fork - LGPL
9279  * <script type="text/javascript">
9280  */
9281 /**
9282  * @class Roo.data.MemoryProxy
9283  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
9284  * to the Reader when its load method is called.
9285  * @constructor
9286  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
9287  */
9288 Roo.data.MemoryProxy = function(data){
9289     if (data.data) {
9290         data = data.data;
9291     }
9292     Roo.data.MemoryProxy.superclass.constructor.call(this);
9293     this.data = data;
9294 };
9295
9296 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
9297     /**
9298      * Load data from the requested source (in this case an in-memory
9299      * data object passed to the constructor), read the data object into
9300      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9301      * process that block using the passed callback.
9302      * @param {Object} params This parameter is not used by the MemoryProxy class.
9303      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9304      * object into a block of Roo.data.Records.
9305      * @param {Function} callback The function into which to pass the block of Roo.data.records.
9306      * The function must be passed <ul>
9307      * <li>The Record block object</li>
9308      * <li>The "arg" argument from the load function</li>
9309      * <li>A boolean success indicator</li>
9310      * </ul>
9311      * @param {Object} scope The scope in which to call the callback
9312      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9313      */
9314     load : function(params, reader, callback, scope, arg){
9315         params = params || {};
9316         var result;
9317         try {
9318             result = reader.readRecords(this.data);
9319         }catch(e){
9320             this.fireEvent("loadexception", this, arg, null, e);
9321             callback.call(scope, null, arg, false);
9322             return;
9323         }
9324         callback.call(scope, result, arg, true);
9325     },
9326     
9327     // private
9328     update : function(params, records){
9329         
9330     }
9331 });/*
9332  * Based on:
9333  * Ext JS Library 1.1.1
9334  * Copyright(c) 2006-2007, Ext JS, LLC.
9335  *
9336  * Originally Released Under LGPL - original licence link has changed is not relivant.
9337  *
9338  * Fork - LGPL
9339  * <script type="text/javascript">
9340  */
9341 /**
9342  * @class Roo.data.HttpProxy
9343  * @extends Roo.data.DataProxy
9344  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
9345  * configured to reference a certain URL.<br><br>
9346  * <p>
9347  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
9348  * from which the running page was served.<br><br>
9349  * <p>
9350  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
9351  * <p>
9352  * Be aware that to enable the browser to parse an XML document, the server must set
9353  * the Content-Type header in the HTTP response to "text/xml".
9354  * @constructor
9355  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
9356  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
9357  * will be used to make the request.
9358  */
9359 Roo.data.HttpProxy = function(conn){
9360     Roo.data.HttpProxy.superclass.constructor.call(this);
9361     // is conn a conn config or a real conn?
9362     this.conn = conn;
9363     this.useAjax = !conn || !conn.events;
9364   
9365 };
9366
9367 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
9368     // thse are take from connection...
9369     
9370     /**
9371      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
9372      */
9373     /**
9374      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
9375      * extra parameters to each request made by this object. (defaults to undefined)
9376      */
9377     /**
9378      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
9379      *  to each request made by this object. (defaults to undefined)
9380      */
9381     /**
9382      * @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)
9383      */
9384     /**
9385      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
9386      */
9387      /**
9388      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
9389      * @type Boolean
9390      */
9391   
9392
9393     /**
9394      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
9395      * @type Boolean
9396      */
9397     /**
9398      * Return the {@link Roo.data.Connection} object being used by this Proxy.
9399      * @return {Connection} The Connection object. This object may be used to subscribe to events on
9400      * a finer-grained basis than the DataProxy events.
9401      */
9402     getConnection : function(){
9403         return this.useAjax ? Roo.Ajax : this.conn;
9404     },
9405
9406     /**
9407      * Load data from the configured {@link Roo.data.Connection}, read the data object into
9408      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
9409      * process that block using the passed callback.
9410      * @param {Object} params An object containing properties which are to be used as HTTP parameters
9411      * for the request to the remote server.
9412      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9413      * object into a block of Roo.data.Records.
9414      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9415      * The function must be passed <ul>
9416      * <li>The Record block object</li>
9417      * <li>The "arg" argument from the load function</li>
9418      * <li>A boolean success indicator</li>
9419      * </ul>
9420      * @param {Object} scope The scope in which to call the callback
9421      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9422      */
9423     load : function(params, reader, callback, scope, arg){
9424         if(this.fireEvent("beforeload", this, params) !== false){
9425             var  o = {
9426                 params : params || {},
9427                 request: {
9428                     callback : callback,
9429                     scope : scope,
9430                     arg : arg
9431                 },
9432                 reader: reader,
9433                 callback : this.loadResponse,
9434                 scope: this
9435             };
9436             if(this.useAjax){
9437                 Roo.applyIf(o, this.conn);
9438                 if(this.activeRequest){
9439                     Roo.Ajax.abort(this.activeRequest);
9440                 }
9441                 this.activeRequest = Roo.Ajax.request(o);
9442             }else{
9443                 this.conn.request(o);
9444             }
9445         }else{
9446             callback.call(scope||this, null, arg, false);
9447         }
9448     },
9449
9450     // private
9451     loadResponse : function(o, success, response){
9452         delete this.activeRequest;
9453         if(!success){
9454             this.fireEvent("loadexception", this, o, response);
9455             o.request.callback.call(o.request.scope, null, o.request.arg, false);
9456             return;
9457         }
9458         var result;
9459         try {
9460             result = o.reader.read(response);
9461         }catch(e){
9462             this.fireEvent("loadexception", this, o, response, e);
9463             o.request.callback.call(o.request.scope, null, o.request.arg, false);
9464             return;
9465         }
9466         
9467         this.fireEvent("load", this, o, o.request.arg);
9468         o.request.callback.call(o.request.scope, result, o.request.arg, true);
9469     },
9470
9471     // private
9472     update : function(dataSet){
9473
9474     },
9475
9476     // private
9477     updateResponse : function(dataSet){
9478
9479     }
9480 });/*
9481  * Based on:
9482  * Ext JS Library 1.1.1
9483  * Copyright(c) 2006-2007, Ext JS, LLC.
9484  *
9485  * Originally Released Under LGPL - original licence link has changed is not relivant.
9486  *
9487  * Fork - LGPL
9488  * <script type="text/javascript">
9489  */
9490
9491 /**
9492  * @class Roo.data.ScriptTagProxy
9493  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
9494  * other than the originating domain of the running page.<br><br>
9495  * <p>
9496  * <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
9497  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
9498  * <p>
9499  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
9500  * source code that is used as the source inside a &lt;script> tag.<br><br>
9501  * <p>
9502  * In order for the browser to process the returned data, the server must wrap the data object
9503  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
9504  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
9505  * depending on whether the callback name was passed:
9506  * <p>
9507  * <pre><code>
9508 boolean scriptTag = false;
9509 String cb = request.getParameter("callback");
9510 if (cb != null) {
9511     scriptTag = true;
9512     response.setContentType("text/javascript");
9513 } else {
9514     response.setContentType("application/x-json");
9515 }
9516 Writer out = response.getWriter();
9517 if (scriptTag) {
9518     out.write(cb + "(");
9519 }
9520 out.print(dataBlock.toJsonString());
9521 if (scriptTag) {
9522     out.write(");");
9523 }
9524 </pre></code>
9525  *
9526  * @constructor
9527  * @param {Object} config A configuration object.
9528  */
9529 Roo.data.ScriptTagProxy = function(config){
9530     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
9531     Roo.apply(this, config);
9532     this.head = document.getElementsByTagName("head")[0];
9533 };
9534
9535 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
9536
9537 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
9538     /**
9539      * @cfg {String} url The URL from which to request the data object.
9540      */
9541     /**
9542      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
9543      */
9544     timeout : 30000,
9545     /**
9546      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
9547      * the server the name of the callback function set up by the load call to process the returned data object.
9548      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
9549      * javascript output which calls this named function passing the data object as its only parameter.
9550      */
9551     callbackParam : "callback",
9552     /**
9553      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
9554      * name to the request.
9555      */
9556     nocache : true,
9557
9558     /**
9559      * Load data from the configured URL, read the data object into
9560      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9561      * process that block using the passed callback.
9562      * @param {Object} params An object containing properties which are to be used as HTTP parameters
9563      * for the request to the remote server.
9564      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9565      * object into a block of Roo.data.Records.
9566      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9567      * The function must be passed <ul>
9568      * <li>The Record block object</li>
9569      * <li>The "arg" argument from the load function</li>
9570      * <li>A boolean success indicator</li>
9571      * </ul>
9572      * @param {Object} scope The scope in which to call the callback
9573      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9574      */
9575     load : function(params, reader, callback, scope, arg){
9576         if(this.fireEvent("beforeload", this, params) !== false){
9577
9578             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
9579
9580             var url = this.url;
9581             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
9582             if(this.nocache){
9583                 url += "&_dc=" + (new Date().getTime());
9584             }
9585             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
9586             var trans = {
9587                 id : transId,
9588                 cb : "stcCallback"+transId,
9589                 scriptId : "stcScript"+transId,
9590                 params : params,
9591                 arg : arg,
9592                 url : url,
9593                 callback : callback,
9594                 scope : scope,
9595                 reader : reader
9596             };
9597             var conn = this;
9598
9599             window[trans.cb] = function(o){
9600                 conn.handleResponse(o, trans);
9601             };
9602
9603             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
9604
9605             if(this.autoAbort !== false){
9606                 this.abort();
9607             }
9608
9609             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
9610
9611             var script = document.createElement("script");
9612             script.setAttribute("src", url);
9613             script.setAttribute("type", "text/javascript");
9614             script.setAttribute("id", trans.scriptId);
9615             this.head.appendChild(script);
9616
9617             this.trans = trans;
9618         }else{
9619             callback.call(scope||this, null, arg, false);
9620         }
9621     },
9622
9623     // private
9624     isLoading : function(){
9625         return this.trans ? true : false;
9626     },
9627
9628     /**
9629      * Abort the current server request.
9630      */
9631     abort : function(){
9632         if(this.isLoading()){
9633             this.destroyTrans(this.trans);
9634         }
9635     },
9636
9637     // private
9638     destroyTrans : function(trans, isLoaded){
9639         this.head.removeChild(document.getElementById(trans.scriptId));
9640         clearTimeout(trans.timeoutId);
9641         if(isLoaded){
9642             window[trans.cb] = undefined;
9643             try{
9644                 delete window[trans.cb];
9645             }catch(e){}
9646         }else{
9647             // if hasn't been loaded, wait for load to remove it to prevent script error
9648             window[trans.cb] = function(){
9649                 window[trans.cb] = undefined;
9650                 try{
9651                     delete window[trans.cb];
9652                 }catch(e){}
9653             };
9654         }
9655     },
9656
9657     // private
9658     handleResponse : function(o, trans){
9659         this.trans = false;
9660         this.destroyTrans(trans, true);
9661         var result;
9662         try {
9663             result = trans.reader.readRecords(o);
9664         }catch(e){
9665             this.fireEvent("loadexception", this, o, trans.arg, e);
9666             trans.callback.call(trans.scope||window, null, trans.arg, false);
9667             return;
9668         }
9669         this.fireEvent("load", this, o, trans.arg);
9670         trans.callback.call(trans.scope||window, result, trans.arg, true);
9671     },
9672
9673     // private
9674     handleFailure : function(trans){
9675         this.trans = false;
9676         this.destroyTrans(trans, false);
9677         this.fireEvent("loadexception", this, null, trans.arg);
9678         trans.callback.call(trans.scope||window, null, trans.arg, false);
9679     }
9680 });/*
9681  * Based on:
9682  * Ext JS Library 1.1.1
9683  * Copyright(c) 2006-2007, Ext JS, LLC.
9684  *
9685  * Originally Released Under LGPL - original licence link has changed is not relivant.
9686  *
9687  * Fork - LGPL
9688  * <script type="text/javascript">
9689  */
9690
9691 /**
9692  * @class Roo.data.JsonReader
9693  * @extends Roo.data.DataReader
9694  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
9695  * based on mappings in a provided Roo.data.Record constructor.
9696  * 
9697  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
9698  * in the reply previously. 
9699  * 
9700  * <p>
9701  * Example code:
9702  * <pre><code>
9703 var RecordDef = Roo.data.Record.create([
9704     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
9705     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
9706 ]);
9707 var myReader = new Roo.data.JsonReader({
9708     totalProperty: "results",    // The property which contains the total dataset size (optional)
9709     root: "rows",                // The property which contains an Array of row objects
9710     id: "id"                     // The property within each row object that provides an ID for the record (optional)
9711 }, RecordDef);
9712 </code></pre>
9713  * <p>
9714  * This would consume a JSON file like this:
9715  * <pre><code>
9716 { 'results': 2, 'rows': [
9717     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
9718     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
9719 }
9720 </code></pre>
9721  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
9722  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
9723  * paged from the remote server.
9724  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
9725  * @cfg {String} root name of the property which contains the Array of row objects.
9726  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
9727  * @constructor
9728  * Create a new JsonReader
9729  * @param {Object} meta Metadata configuration options
9730  * @param {Object} recordType Either an Array of field definition objects,
9731  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
9732  */
9733 Roo.data.JsonReader = function(meta, recordType){
9734     
9735     meta = meta || {};
9736     // set some defaults:
9737     Roo.applyIf(meta, {
9738         totalProperty: 'total',
9739         successProperty : 'success',
9740         root : 'data',
9741         id : 'id'
9742     });
9743     
9744     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
9745 };
9746 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
9747     
9748     /**
9749      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
9750      * Used by Store query builder to append _requestMeta to params.
9751      * 
9752      */
9753     metaFromRemote : false,
9754     /**
9755      * This method is only used by a DataProxy which has retrieved data from a remote server.
9756      * @param {Object} response The XHR object which contains the JSON data in its responseText.
9757      * @return {Object} data A data block which is used by an Roo.data.Store object as
9758      * a cache of Roo.data.Records.
9759      */
9760     read : function(response){
9761         var json = response.responseText;
9762        
9763         var o = /* eval:var:o */ eval("("+json+")");
9764         if(!o) {
9765             throw {message: "JsonReader.read: Json object not found"};
9766         }
9767         
9768         if(o.metaData){
9769             
9770             delete this.ef;
9771             this.metaFromRemote = true;
9772             this.meta = o.metaData;
9773             this.recordType = Roo.data.Record.create(o.metaData.fields);
9774             this.onMetaChange(this.meta, this.recordType, o);
9775         }
9776         return this.readRecords(o);
9777     },
9778
9779     // private function a store will implement
9780     onMetaChange : function(meta, recordType, o){
9781
9782     },
9783
9784     /**
9785          * @ignore
9786          */
9787     simpleAccess: function(obj, subsc) {
9788         return obj[subsc];
9789     },
9790
9791         /**
9792          * @ignore
9793          */
9794     getJsonAccessor: function(){
9795         var re = /[\[\.]/;
9796         return function(expr) {
9797             try {
9798                 return(re.test(expr))
9799                     ? new Function("obj", "return obj." + expr)
9800                     : function(obj){
9801                         return obj[expr];
9802                     };
9803             } catch(e){}
9804             return Roo.emptyFn;
9805         };
9806     }(),
9807
9808     /**
9809      * Create a data block containing Roo.data.Records from an XML document.
9810      * @param {Object} o An object which contains an Array of row objects in the property specified
9811      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
9812      * which contains the total size of the dataset.
9813      * @return {Object} data A data block which is used by an Roo.data.Store object as
9814      * a cache of Roo.data.Records.
9815      */
9816     readRecords : function(o){
9817         /**
9818          * After any data loads, the raw JSON data is available for further custom processing.
9819          * @type Object
9820          */
9821         this.o = o;
9822         var s = this.meta, Record = this.recordType,
9823             f = Record.prototype.fields, fi = f.items, fl = f.length;
9824
9825 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
9826         if (!this.ef) {
9827             if(s.totalProperty) {
9828                     this.getTotal = this.getJsonAccessor(s.totalProperty);
9829                 }
9830                 if(s.successProperty) {
9831                     this.getSuccess = this.getJsonAccessor(s.successProperty);
9832                 }
9833                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
9834                 if (s.id) {
9835                         var g = this.getJsonAccessor(s.id);
9836                         this.getId = function(rec) {
9837                                 var r = g(rec);
9838                                 return (r === undefined || r === "") ? null : r;
9839                         };
9840                 } else {
9841                         this.getId = function(){return null;};
9842                 }
9843             this.ef = [];
9844             for(var jj = 0; jj < fl; jj++){
9845                 f = fi[jj];
9846                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
9847                 this.ef[jj] = this.getJsonAccessor(map);
9848             }
9849         }
9850
9851         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
9852         if(s.totalProperty){
9853             var vt = parseInt(this.getTotal(o), 10);
9854             if(!isNaN(vt)){
9855                 totalRecords = vt;
9856             }
9857         }
9858         if(s.successProperty){
9859             var vs = this.getSuccess(o);
9860             if(vs === false || vs === 'false'){
9861                 success = false;
9862             }
9863         }
9864         var records = [];
9865             for(var i = 0; i < c; i++){
9866                     var n = root[i];
9867                 var values = {};
9868                 var id = this.getId(n);
9869                 for(var j = 0; j < fl; j++){
9870                     f = fi[j];
9871                 var v = this.ef[j](n);
9872                 if (!f.convert) {
9873                     Roo.log('missing convert for ' + f.name);
9874                     Roo.log(f);
9875                     continue;
9876                 }
9877                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
9878                 }
9879                 var record = new Record(values, id);
9880                 record.json = n;
9881                 records[i] = record;
9882             }
9883             return {
9884             raw : o,
9885                 success : success,
9886                 records : records,
9887                 totalRecords : totalRecords
9888             };
9889     }
9890 });/*
9891  * Based on:
9892  * Ext JS Library 1.1.1
9893  * Copyright(c) 2006-2007, Ext JS, LLC.
9894  *
9895  * Originally Released Under LGPL - original licence link has changed is not relivant.
9896  *
9897  * Fork - LGPL
9898  * <script type="text/javascript">
9899  */
9900
9901 /**
9902  * @class Roo.data.ArrayReader
9903  * @extends Roo.data.DataReader
9904  * Data reader class to create an Array of Roo.data.Record objects from an Array.
9905  * Each element of that Array represents a row of data fields. The
9906  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
9907  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
9908  * <p>
9909  * Example code:.
9910  * <pre><code>
9911 var RecordDef = Roo.data.Record.create([
9912     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
9913     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
9914 ]);
9915 var myReader = new Roo.data.ArrayReader({
9916     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
9917 }, RecordDef);
9918 </code></pre>
9919  * <p>
9920  * This would consume an Array like this:
9921  * <pre><code>
9922 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
9923   </code></pre>
9924  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
9925  * @constructor
9926  * Create a new JsonReader
9927  * @param {Object} meta Metadata configuration options.
9928  * @param {Object} recordType Either an Array of field definition objects
9929  * as specified to {@link Roo.data.Record#create},
9930  * or an {@link Roo.data.Record} object
9931  * created using {@link Roo.data.Record#create}.
9932  */
9933 Roo.data.ArrayReader = function(meta, recordType){
9934     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
9935 };
9936
9937 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
9938     /**
9939      * Create a data block containing Roo.data.Records from an XML document.
9940      * @param {Object} o An Array of row objects which represents the dataset.
9941      * @return {Object} data A data block which is used by an Roo.data.Store object as
9942      * a cache of Roo.data.Records.
9943      */
9944     readRecords : function(o){
9945         var sid = this.meta ? this.meta.id : null;
9946         var recordType = this.recordType, fields = recordType.prototype.fields;
9947         var records = [];
9948         var root = o;
9949             for(var i = 0; i < root.length; i++){
9950                     var n = root[i];
9951                 var values = {};
9952                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
9953                 for(var j = 0, jlen = fields.length; j < jlen; j++){
9954                 var f = fields.items[j];
9955                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
9956                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
9957                 v = f.convert(v);
9958                 values[f.name] = v;
9959             }
9960                 var record = new recordType(values, id);
9961                 record.json = n;
9962                 records[records.length] = record;
9963             }
9964             return {
9965                 records : records,
9966                 totalRecords : records.length
9967             };
9968     }
9969 });/*
9970  * - LGPL
9971  * * 
9972  */
9973
9974 /**
9975  * @class Roo.bootstrap.ComboBox
9976  * @extends Roo.bootstrap.TriggerField
9977  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
9978  * @cfg {Boolean} append (true|false) default false
9979  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
9980  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
9981  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
9982  * @constructor
9983  * Create a new ComboBox.
9984  * @param {Object} config Configuration options
9985  */
9986 Roo.bootstrap.ComboBox = function(config){
9987     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
9988     this.addEvents({
9989         /**
9990          * @event expand
9991          * Fires when the dropdown list is expanded
9992              * @param {Roo.bootstrap.ComboBox} combo This combo box
9993              */
9994         'expand' : true,
9995         /**
9996          * @event collapse
9997          * Fires when the dropdown list is collapsed
9998              * @param {Roo.bootstrap.ComboBox} combo This combo box
9999              */
10000         'collapse' : true,
10001         /**
10002          * @event beforeselect
10003          * Fires before a list item is selected. Return false to cancel the selection.
10004              * @param {Roo.bootstrap.ComboBox} combo This combo box
10005              * @param {Roo.data.Record} record The data record returned from the underlying store
10006              * @param {Number} index The index of the selected item in the dropdown list
10007              */
10008         'beforeselect' : true,
10009         /**
10010          * @event select
10011          * Fires when a list item is selected
10012              * @param {Roo.bootstrap.ComboBox} combo This combo box
10013              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
10014              * @param {Number} index The index of the selected item in the dropdown list
10015              */
10016         'select' : true,
10017         /**
10018          * @event beforequery
10019          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
10020          * The event object passed has these properties:
10021              * @param {Roo.bootstrap.ComboBox} combo This combo box
10022              * @param {String} query The query
10023              * @param {Boolean} forceAll true to force "all" query
10024              * @param {Boolean} cancel true to cancel the query
10025              * @param {Object} e The query event object
10026              */
10027         'beforequery': true,
10028          /**
10029          * @event add
10030          * Fires when the 'add' icon is pressed (add a listener to enable add button)
10031              * @param {Roo.bootstrap.ComboBox} combo This combo box
10032              */
10033         'add' : true,
10034         /**
10035          * @event edit
10036          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
10037              * @param {Roo.bootstrap.ComboBox} combo This combo box
10038              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
10039              */
10040         'edit' : true,
10041         /**
10042          * @event remove
10043          * Fires when the remove value from the combobox array
10044              * @param {Roo.bootstrap.ComboBox} combo This combo box
10045              */
10046         'remove' : true
10047         
10048     });
10049     
10050     this.item = [];
10051     this.tickItems = [];
10052     
10053     this.selectedIndex = -1;
10054     if(this.mode == 'local'){
10055         if(config.queryDelay === undefined){
10056             this.queryDelay = 10;
10057         }
10058         if(config.minChars === undefined){
10059             this.minChars = 0;
10060         }
10061     }
10062 };
10063
10064 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
10065      
10066     /**
10067      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
10068      * rendering into an Roo.Editor, defaults to false)
10069      */
10070     /**
10071      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
10072      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
10073      */
10074     /**
10075      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
10076      */
10077     /**
10078      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
10079      * the dropdown list (defaults to undefined, with no header element)
10080      */
10081
10082      /**
10083      * @cfg {String/Roo.Template} tpl The template to use to render the output
10084      */
10085      
10086      /**
10087      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
10088      */
10089     listWidth: undefined,
10090     /**
10091      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
10092      * mode = 'remote' or 'text' if mode = 'local')
10093      */
10094     displayField: undefined,
10095     /**
10096      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
10097      * mode = 'remote' or 'value' if mode = 'local'). 
10098      * Note: use of a valueField requires the user make a selection
10099      * in order for a value to be mapped.
10100      */
10101     valueField: undefined,
10102     
10103     
10104     /**
10105      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
10106      * field's data value (defaults to the underlying DOM element's name)
10107      */
10108     hiddenName: undefined,
10109     /**
10110      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
10111      */
10112     listClass: '',
10113     /**
10114      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
10115      */
10116     selectedClass: 'active',
10117     
10118     /**
10119      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
10120      */
10121     shadow:'sides',
10122     /**
10123      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
10124      * anchor positions (defaults to 'tl-bl')
10125      */
10126     listAlign: 'tl-bl?',
10127     /**
10128      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
10129      */
10130     maxHeight: 300,
10131     /**
10132      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
10133      * query specified by the allQuery config option (defaults to 'query')
10134      */
10135     triggerAction: 'query',
10136     /**
10137      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
10138      * (defaults to 4, does not apply if editable = false)
10139      */
10140     minChars : 4,
10141     /**
10142      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
10143      * delay (typeAheadDelay) if it matches a known value (defaults to false)
10144      */
10145     typeAhead: false,
10146     /**
10147      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
10148      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
10149      */
10150     queryDelay: 500,
10151     /**
10152      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
10153      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
10154      */
10155     pageSize: 0,
10156     /**
10157      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
10158      * when editable = true (defaults to false)
10159      */
10160     selectOnFocus:false,
10161     /**
10162      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
10163      */
10164     queryParam: 'query',
10165     /**
10166      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
10167      * when mode = 'remote' (defaults to 'Loading...')
10168      */
10169     loadingText: 'Loading...',
10170     /**
10171      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
10172      */
10173     resizable: false,
10174     /**
10175      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
10176      */
10177     handleHeight : 8,
10178     /**
10179      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
10180      * traditional select (defaults to true)
10181      */
10182     editable: true,
10183     /**
10184      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
10185      */
10186     allQuery: '',
10187     /**
10188      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
10189      */
10190     mode: 'remote',
10191     /**
10192      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
10193      * listWidth has a higher value)
10194      */
10195     minListWidth : 70,
10196     /**
10197      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
10198      * allow the user to set arbitrary text into the field (defaults to false)
10199      */
10200     forceSelection:false,
10201     /**
10202      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
10203      * if typeAhead = true (defaults to 250)
10204      */
10205     typeAheadDelay : 250,
10206     /**
10207      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
10208      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
10209      */
10210     valueNotFoundText : undefined,
10211     /**
10212      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
10213      */
10214     blockFocus : false,
10215     
10216     /**
10217      * @cfg {Boolean} disableClear Disable showing of clear button.
10218      */
10219     disableClear : false,
10220     /**
10221      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
10222      */
10223     alwaysQuery : false,
10224     
10225     /**
10226      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
10227      */
10228     multiple : false,
10229     
10230     //private
10231     addicon : false,
10232     editicon: false,
10233     
10234     page: 0,
10235     hasQuery: false,
10236     append: false,
10237     loadNext: false,
10238     autoFocus : true,
10239     tickable : false,
10240     btnPosition : 'right',
10241     
10242     // element that contains real text value.. (when hidden is used..)
10243     
10244     getAutoCreate : function()
10245     {
10246         var cfg = false;
10247         
10248         /*
10249          *  Normal ComboBox
10250          */
10251         if(!this.tickable){
10252             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
10253             return cfg;
10254         }
10255         
10256         /*
10257          *  ComboBox with tickable selections
10258          */
10259              
10260         var align = this.labelAlign || this.parentLabelAlign();
10261         
10262         cfg = {
10263             cls : 'form-group roo-combobox-tickable' //input-group
10264         };
10265         
10266         
10267         var buttons = {
10268             tag : 'div',
10269             cls : 'tickable-buttons',
10270             cn : [
10271                 {
10272                     tag : 'button',
10273                     type : 'button',
10274                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
10275                     html : 'Edit'
10276                 },
10277                 {
10278                     tag : 'button',
10279                     type : 'button',
10280                     name : 'ok',
10281                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
10282                     html : 'Done'
10283                 },
10284                 {
10285                     tag : 'button',
10286                     type : 'button',
10287                     name : 'cancel',
10288                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
10289                     html : 'Cancel'
10290                 }
10291             ]
10292         };
10293         
10294         var _this = this;
10295         Roo.each(buttons.cn, function(c){
10296             if (_this.size) {
10297                 c.cls += ' btn-' + _this.size;
10298             }
10299
10300             if (_this.disabled) {
10301                 c.disabled = true;
10302             }
10303         });
10304         
10305         var box = {
10306             tag: 'div',
10307             cn: [
10308                 {
10309                     tag: 'input',
10310                     type : 'hidden',
10311                     cls: 'form-hidden-field'
10312                 },
10313                 {
10314                     tag: 'ul',
10315                     cls: 'select2-choices',
10316                     cn:[
10317                         {
10318                             tag: 'li',
10319                             cls: 'select2-search-field',
10320                             cn: [
10321
10322                                 buttons
10323                             ]
10324                         }
10325                     ]
10326                 }
10327             ]
10328         }
10329         
10330         var combobox = {
10331             cls: 'select2-container input-group select2-container-multi',
10332             cn: [
10333                 box,
10334                 {
10335                     tag: 'ul',
10336                     cls: 'typeahead typeahead-long dropdown-menu',
10337                     style: 'display:none; max-height:' + this.maxHeight + 'px;'
10338                 }
10339             ]
10340         };
10341         
10342         if (align ==='left' && this.fieldLabel.length) {
10343             
10344                 Roo.log("left and has label");
10345                 cfg.cn = [
10346                     
10347                     {
10348                         tag: 'label',
10349                         'for' :  id,
10350                         cls : 'control-label col-sm-' + this.labelWidth,
10351                         html : this.fieldLabel
10352                         
10353                     },
10354                     {
10355                         cls : "col-sm-" + (12 - this.labelWidth), 
10356                         cn: [
10357                             combobox
10358                         ]
10359                     }
10360                     
10361                 ];
10362         } else if ( this.fieldLabel.length) {
10363                 Roo.log(" label");
10364                  cfg.cn = [
10365                    
10366                     {
10367                         tag: 'label',
10368                         //cls : 'input-group-addon',
10369                         html : this.fieldLabel
10370                         
10371                     },
10372                     
10373                     combobox
10374                     
10375                 ];
10376
10377         } else {
10378             
10379                 Roo.log(" no label && no align");
10380                 cfg = combobox
10381                      
10382                 
10383         }
10384          
10385         var settings=this;
10386         ['xs','sm','md','lg'].map(function(size){
10387             if (settings[size]) {
10388                 cfg.cls += ' col-' + size + '-' + settings[size];
10389             }
10390         });
10391         
10392         return cfg;
10393         
10394     },
10395     
10396     // private
10397     initEvents: function()
10398     {
10399         
10400         if (!this.store) {
10401             throw "can not find store for combo";
10402         }
10403         this.store = Roo.factory(this.store, Roo.data);
10404         
10405         if(this.tickable){
10406             this.initTickableEvnets();
10407             return;
10408         }
10409         
10410         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
10411         
10412         
10413         if(this.hiddenName){
10414             
10415             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10416             
10417             this.hiddenField.dom.value =
10418                 this.hiddenValue !== undefined ? this.hiddenValue :
10419                 this.value !== undefined ? this.value : '';
10420
10421             // prevent input submission
10422             this.el.dom.removeAttribute('name');
10423             this.hiddenField.dom.setAttribute('name', this.hiddenName);
10424              
10425              
10426         }
10427         //if(Roo.isGecko){
10428         //    this.el.dom.setAttribute('autocomplete', 'off');
10429         //}
10430
10431         var cls = 'x-combo-list';
10432         this.list = this.el.select('ul.dropdown-menu',true).first();
10433
10434         //this.list = new Roo.Layer({
10435         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
10436         //});
10437         
10438         var _this = this;
10439         
10440         (function(){
10441             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
10442             _this.list.setWidth(lw);
10443         }).defer(100);
10444         
10445         this.list.on('mouseover', this.onViewOver, this);
10446         this.list.on('mousemove', this.onViewMove, this);
10447         
10448         this.list.on('scroll', this.onViewScroll, this);
10449         
10450         /*
10451         this.list.swallowEvent('mousewheel');
10452         this.assetHeight = 0;
10453
10454         if(this.title){
10455             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
10456             this.assetHeight += this.header.getHeight();
10457         }
10458
10459         this.innerList = this.list.createChild({cls:cls+'-inner'});
10460         this.innerList.on('mouseover', this.onViewOver, this);
10461         this.innerList.on('mousemove', this.onViewMove, this);
10462         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
10463         
10464         if(this.allowBlank && !this.pageSize && !this.disableClear){
10465             this.footer = this.list.createChild({cls:cls+'-ft'});
10466             this.pageTb = new Roo.Toolbar(this.footer);
10467            
10468         }
10469         if(this.pageSize){
10470             this.footer = this.list.createChild({cls:cls+'-ft'});
10471             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
10472                     {pageSize: this.pageSize});
10473             
10474         }
10475         
10476         if (this.pageTb && this.allowBlank && !this.disableClear) {
10477             var _this = this;
10478             this.pageTb.add(new Roo.Toolbar.Fill(), {
10479                 cls: 'x-btn-icon x-btn-clear',
10480                 text: '&#160;',
10481                 handler: function()
10482                 {
10483                     _this.collapse();
10484                     _this.clearValue();
10485                     _this.onSelect(false, -1);
10486                 }
10487             });
10488         }
10489         if (this.footer) {
10490             this.assetHeight += this.footer.getHeight();
10491         }
10492         */
10493             
10494         if(!this.tpl){
10495             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
10496         }
10497
10498         this.view = new Roo.View(this.el.select('ul.dropdown-menu',true).first(), this.tpl, {
10499             singleSelect:true, store: this.store, selectedClass: this.selectedClass
10500         });
10501         //this.view.wrapEl.setDisplayed(false);
10502         this.view.on('click', this.onViewClick, this);
10503         
10504         
10505         
10506         this.store.on('beforeload', this.onBeforeLoad, this);
10507         this.store.on('load', this.onLoad, this);
10508         this.store.on('loadexception', this.onLoadException, this);
10509         /*
10510         if(this.resizable){
10511             this.resizer = new Roo.Resizable(this.list,  {
10512                pinned:true, handles:'se'
10513             });
10514             this.resizer.on('resize', function(r, w, h){
10515                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
10516                 this.listWidth = w;
10517                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
10518                 this.restrictHeight();
10519             }, this);
10520             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
10521         }
10522         */
10523         if(!this.editable){
10524             this.editable = true;
10525             this.setEditable(false);
10526         }
10527         
10528         /*
10529         
10530         if (typeof(this.events.add.listeners) != 'undefined') {
10531             
10532             this.addicon = this.wrap.createChild(
10533                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
10534        
10535             this.addicon.on('click', function(e) {
10536                 this.fireEvent('add', this);
10537             }, this);
10538         }
10539         if (typeof(this.events.edit.listeners) != 'undefined') {
10540             
10541             this.editicon = this.wrap.createChild(
10542                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
10543             if (this.addicon) {
10544                 this.editicon.setStyle('margin-left', '40px');
10545             }
10546             this.editicon.on('click', function(e) {
10547                 
10548                 // we fire even  if inothing is selected..
10549                 this.fireEvent('edit', this, this.lastData );
10550                 
10551             }, this);
10552         }
10553         */
10554         
10555         this.keyNav = new Roo.KeyNav(this.inputEl(), {
10556             "up" : function(e){
10557                 this.inKeyMode = true;
10558                 this.selectPrev();
10559             },
10560
10561             "down" : function(e){
10562                 if(!this.isExpanded()){
10563                     this.onTriggerClick();
10564                 }else{
10565                     this.inKeyMode = true;
10566                     this.selectNext();
10567                 }
10568             },
10569
10570             "enter" : function(e){
10571 //                this.onViewClick();
10572                 //return true;
10573                 this.collapse();
10574                 
10575                 if(this.fireEvent("specialkey", this, e)){
10576                     this.onViewClick(false);
10577                 }
10578                 
10579                 return true;
10580             },
10581
10582             "esc" : function(e){
10583                 this.collapse();
10584             },
10585
10586             "tab" : function(e){
10587                 this.collapse();
10588                 
10589                 if(this.fireEvent("specialkey", this, e)){
10590                     this.onViewClick(false);
10591                 }
10592                 
10593                 return true;
10594             },
10595
10596             scope : this,
10597
10598             doRelay : function(foo, bar, hname){
10599                 if(hname == 'down' || this.scope.isExpanded()){
10600                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
10601                 }
10602                 return true;
10603             },
10604
10605             forceKeyDown: true
10606         });
10607         
10608         
10609         this.queryDelay = Math.max(this.queryDelay || 10,
10610                 this.mode == 'local' ? 10 : 250);
10611         
10612         
10613         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
10614         
10615         if(this.typeAhead){
10616             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
10617         }
10618         if(this.editable !== false){
10619             this.inputEl().on("keyup", this.onKeyUp, this);
10620         }
10621         if(this.forceSelection){
10622             this.inputEl().on('blur', this.doForce, this);
10623         }
10624         
10625         if(this.multiple){
10626             this.choices = this.el.select('ul.select2-choices', true).first();
10627             this.searchField = this.el.select('ul li.select2-search-field', true).first();
10628         }
10629     },
10630     
10631     initTickableEvnets: function()
10632     {   
10633         if(this.hiddenName){
10634             
10635             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10636             
10637             this.hiddenField.dom.value =
10638                 this.hiddenValue !== undefined ? this.hiddenValue :
10639                 this.value !== undefined ? this.value : '';
10640
10641             // prevent input submission
10642             this.el.dom.removeAttribute('name');
10643             this.hiddenField.dom.setAttribute('name', this.hiddenName);
10644              
10645              
10646         }
10647         
10648         this.list = this.el.select('ul.dropdown-menu',true).first();
10649         
10650         this.choices = this.el.select('ul.select2-choices', true).first();
10651         this.searchField = this.el.select('ul li.select2-search-field', true).first();
10652         
10653         this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
10654          
10655         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
10656         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
10657         
10658         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
10659         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
10660         
10661         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
10662         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
10663         
10664         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
10665         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
10666         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
10667         
10668         this.okBtn.hide();
10669         this.cancelBtn.hide();
10670         
10671         var _this = this;
10672         
10673         (function(){
10674             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
10675             _this.list.setWidth(lw);
10676         }).defer(100);
10677         
10678         this.list.on('mouseover', this.onViewOver, this);
10679         this.list.on('mousemove', this.onViewMove, this);
10680         
10681         this.list.on('scroll', this.onViewScroll, this);
10682         
10683         if(!this.tpl){
10684             this.tpl = '<li class="select2-result"><div class="checkbox"><input id="{roo-id}" type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></li>';
10685         }
10686
10687         this.view = new Roo.View(this.el.select('ul.dropdown-menu',true).first(), this.tpl, {
10688             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
10689         });
10690         
10691         //this.view.wrapEl.setDisplayed(false);
10692         this.view.on('click', this.onViewClick, this);
10693         
10694         
10695         
10696         this.store.on('beforeload', this.onBeforeLoad, this);
10697         this.store.on('load', this.onLoad, this);
10698         this.store.on('loadexception', this.onLoadException, this);
10699         
10700 //        this.keyNav = new Roo.KeyNav(this.inputEl(), {
10701 //            "up" : function(e){
10702 //                this.inKeyMode = true;
10703 //                this.selectPrev();
10704 //            },
10705 //
10706 //            "down" : function(e){
10707 //                if(!this.isExpanded()){
10708 //                    this.onTriggerClick();
10709 //                }else{
10710 //                    this.inKeyMode = true;
10711 //                    this.selectNext();
10712 //                }
10713 //            },
10714 //
10715 //            "enter" : function(e){
10716 ////                this.onViewClick();
10717 //                //return true;
10718 //                this.collapse();
10719 //                
10720 //                if(this.fireEvent("specialkey", this, e)){
10721 //                    this.onViewClick(false);
10722 //                }
10723 //                
10724 //                return true;
10725 //            },
10726 //
10727 //            "esc" : function(e){
10728 //                this.collapse();
10729 //            },
10730 //
10731 //            "tab" : function(e){
10732 //                this.collapse();
10733 //                
10734 //                if(this.fireEvent("specialkey", this, e)){
10735 //                    this.onViewClick(false);
10736 //                }
10737 //                
10738 //                return true;
10739 //            },
10740 //
10741 //            scope : this,
10742 //
10743 //            doRelay : function(foo, bar, hname){
10744 //                if(hname == 'down' || this.scope.isExpanded()){
10745 //                   return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
10746 //                }
10747 //                return true;
10748 //            },
10749 //
10750 //            forceKeyDown: true
10751 //        });
10752         
10753         
10754         this.queryDelay = Math.max(this.queryDelay || 10,
10755                 this.mode == 'local' ? 10 : 250);
10756         
10757         
10758         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
10759         
10760         if(this.typeAhead){
10761             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
10762         }
10763     },
10764
10765     onDestroy : function(){
10766         if(this.view){
10767             this.view.setStore(null);
10768             this.view.el.removeAllListeners();
10769             this.view.el.remove();
10770             this.view.purgeListeners();
10771         }
10772         if(this.list){
10773             this.list.dom.innerHTML  = '';
10774         }
10775         
10776         if(this.store){
10777             this.store.un('beforeload', this.onBeforeLoad, this);
10778             this.store.un('load', this.onLoad, this);
10779             this.store.un('loadexception', this.onLoadException, this);
10780         }
10781         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
10782     },
10783
10784     // private
10785     fireKey : function(e){
10786         if(e.isNavKeyPress() && !this.list.isVisible()){
10787             this.fireEvent("specialkey", this, e);
10788         }
10789     },
10790
10791     // private
10792     onResize: function(w, h){
10793 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
10794 //        
10795 //        if(typeof w != 'number'){
10796 //            // we do not handle it!?!?
10797 //            return;
10798 //        }
10799 //        var tw = this.trigger.getWidth();
10800 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
10801 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
10802 //        var x = w - tw;
10803 //        this.inputEl().setWidth( this.adjustWidth('input', x));
10804 //            
10805 //        //this.trigger.setStyle('left', x+'px');
10806 //        
10807 //        if(this.list && this.listWidth === undefined){
10808 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
10809 //            this.list.setWidth(lw);
10810 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
10811 //        }
10812         
10813     
10814         
10815     },
10816
10817     /**
10818      * Allow or prevent the user from directly editing the field text.  If false is passed,
10819      * the user will only be able to select from the items defined in the dropdown list.  This method
10820      * is the runtime equivalent of setting the 'editable' config option at config time.
10821      * @param {Boolean} value True to allow the user to directly edit the field text
10822      */
10823     setEditable : function(value){
10824         if(value == this.editable){
10825             return;
10826         }
10827         this.editable = value;
10828         if(!value){
10829             this.inputEl().dom.setAttribute('readOnly', true);
10830             this.inputEl().on('mousedown', this.onTriggerClick,  this);
10831             this.inputEl().addClass('x-combo-noedit');
10832         }else{
10833             this.inputEl().dom.setAttribute('readOnly', false);
10834             this.inputEl().un('mousedown', this.onTriggerClick,  this);
10835             this.inputEl().removeClass('x-combo-noedit');
10836         }
10837     },
10838
10839     // private
10840     
10841     onBeforeLoad : function(combo,opts){
10842         if(!this.hasFocus){
10843             return;
10844         }
10845          if (!opts.add) {
10846             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
10847          }
10848         this.restrictHeight();
10849         this.selectedIndex = -1;
10850     },
10851
10852     // private
10853     onLoad : function(){
10854         
10855         this.hasQuery = false;
10856         
10857         if(!this.hasFocus){
10858             return;
10859         }
10860         
10861         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
10862             this.loading.hide();
10863         }
10864         
10865         if(this.store.getCount() > 0){
10866             this.expand();
10867             this.restrictHeight();
10868             if(this.lastQuery == this.allQuery){
10869                 if(this.editable && !this.tickable){
10870                     this.inputEl().dom.select();
10871                 }
10872                 if(!this.selectByValue(this.value, true) && this.autoFocus){
10873                     this.select(0, true);
10874                 }
10875             }else{
10876                 if(this.autoFocus){
10877                     this.selectNext();
10878                 }
10879                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
10880                     this.taTask.delay(this.typeAheadDelay);
10881                 }
10882             }
10883         }else{
10884             this.onEmptyResults();
10885         }
10886         
10887         //this.el.focus();
10888     },
10889     // private
10890     onLoadException : function()
10891     {
10892         this.hasQuery = false;
10893         
10894         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
10895             this.loading.hide();
10896         }
10897         
10898         this.collapse();
10899         Roo.log(this.store.reader.jsonData);
10900         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
10901             // fixme
10902             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
10903         }
10904         
10905         
10906     },
10907     // private
10908     onTypeAhead : function(){
10909         if(this.store.getCount() > 0){
10910             var r = this.store.getAt(0);
10911             var newValue = r.data[this.displayField];
10912             var len = newValue.length;
10913             var selStart = this.getRawValue().length;
10914             
10915             if(selStart != len){
10916                 this.setRawValue(newValue);
10917                 this.selectText(selStart, newValue.length);
10918             }
10919         }
10920     },
10921
10922     // private
10923     onSelect : function(record, index){
10924         
10925         if(this.fireEvent('beforeselect', this, record, index) !== false){
10926         
10927             this.setFromData(index > -1 ? record.data : false);
10928             
10929             this.collapse();
10930             this.fireEvent('select', this, record, index);
10931         }
10932     },
10933
10934     /**
10935      * Returns the currently selected field value or empty string if no value is set.
10936      * @return {String} value The selected value
10937      */
10938     getValue : function(){
10939         
10940         if(this.multiple){
10941             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
10942         }
10943         
10944         if(this.valueField){
10945             return typeof this.value != 'undefined' ? this.value : '';
10946         }else{
10947             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
10948         }
10949     },
10950
10951     /**
10952      * Clears any text/value currently set in the field
10953      */
10954     clearValue : function(){
10955         if(this.hiddenField){
10956             this.hiddenField.dom.value = '';
10957         }
10958         this.value = '';
10959         this.setRawValue('');
10960         this.lastSelectionText = '';
10961         
10962     },
10963
10964     /**
10965      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
10966      * will be displayed in the field.  If the value does not match the data value of an existing item,
10967      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
10968      * Otherwise the field will be blank (although the value will still be set).
10969      * @param {String} value The value to match
10970      */
10971     setValue : function(v){
10972         if(this.multiple){
10973             this.syncValue();
10974             return;
10975         }
10976         
10977         var text = v;
10978         if(this.valueField){
10979             var r = this.findRecord(this.valueField, v);
10980             if(r){
10981                 text = r.data[this.displayField];
10982             }else if(this.valueNotFoundText !== undefined){
10983                 text = this.valueNotFoundText;
10984             }
10985         }
10986         this.lastSelectionText = text;
10987         if(this.hiddenField){
10988             this.hiddenField.dom.value = v;
10989         }
10990         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
10991         this.value = v;
10992     },
10993     /**
10994      * @property {Object} the last set data for the element
10995      */
10996     
10997     lastData : false,
10998     /**
10999      * Sets the value of the field based on a object which is related to the record format for the store.
11000      * @param {Object} value the value to set as. or false on reset?
11001      */
11002     setFromData : function(o){
11003         
11004         if(this.multiple){
11005             this.addItem(o);
11006             return;
11007         }
11008             
11009         var dv = ''; // display value
11010         var vv = ''; // value value..
11011         this.lastData = o;
11012         if (this.displayField) {
11013             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11014         } else {
11015             // this is an error condition!!!
11016             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
11017         }
11018         
11019         if(this.valueField){
11020             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
11021         }
11022         
11023         if(this.hiddenField){
11024             this.hiddenField.dom.value = vv;
11025             
11026             this.lastSelectionText = dv;
11027             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11028             this.value = vv;
11029             return;
11030         }
11031         // no hidden field.. - we store the value in 'value', but still display
11032         // display field!!!!
11033         this.lastSelectionText = dv;
11034         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11035         this.value = vv;
11036         
11037         
11038     },
11039     // private
11040     reset : function(){
11041         // overridden so that last data is reset..
11042         this.setValue(this.originalValue);
11043         this.clearInvalid();
11044         this.lastData = false;
11045         if (this.view) {
11046             this.view.clearSelections();
11047         }
11048     },
11049     // private
11050     findRecord : function(prop, value){
11051         var record;
11052         if(this.store.getCount() > 0){
11053             this.store.each(function(r){
11054                 if(r.data[prop] == value){
11055                     record = r;
11056                     return false;
11057                 }
11058                 return true;
11059             });
11060         }
11061         return record;
11062     },
11063     
11064     getName: function()
11065     {
11066         // returns hidden if it's set..
11067         if (!this.rendered) {return ''};
11068         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
11069         
11070     },
11071     // private
11072     onViewMove : function(e, t){
11073         this.inKeyMode = false;
11074     },
11075
11076     // private
11077     onViewOver : function(e, t){
11078         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
11079             return;
11080         }
11081         var item = this.view.findItemFromChild(t);
11082         
11083         if(item){
11084             var index = this.view.indexOf(item);
11085             this.select(index, false);
11086         }
11087     },
11088
11089     // private
11090     onViewClick : function(view, doFocus, el, e)
11091     {
11092         var index = this.view.getSelectedIndexes()[0];
11093         
11094         var r = this.store.getAt(index);
11095         
11096         if(this.tickable){
11097             
11098             if(e.getTarget().nodeName.toLowerCase() != 'input'){
11099                 return;
11100             }
11101             
11102             var rm = false;
11103             var _this = this;
11104             
11105             Roo.each(this.tickItems, function(v,k){
11106                 
11107                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
11108                     _this.tickItems.splice(k, 1);
11109                     rm = true;
11110                     return;
11111                 }
11112             })
11113             
11114             if(rm){
11115                 return;
11116             }
11117             
11118             this.tickItems.push(r.data);
11119             return;
11120         }
11121         
11122         if(r){
11123             this.onSelect(r, index);
11124         }
11125         if(doFocus !== false && !this.blockFocus){
11126             this.inputEl().focus();
11127         }
11128     },
11129
11130     // private
11131     restrictHeight : function(){
11132         //this.innerList.dom.style.height = '';
11133         //var inner = this.innerList.dom;
11134         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
11135         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
11136         //this.list.beginUpdate();
11137         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
11138         this.list.alignTo(this.inputEl(), this.listAlign);
11139         //this.list.endUpdate();
11140     },
11141
11142     // private
11143     onEmptyResults : function(){
11144         this.collapse();
11145     },
11146
11147     /**
11148      * Returns true if the dropdown list is expanded, else false.
11149      */
11150     isExpanded : function(){
11151         return this.list.isVisible();
11152     },
11153
11154     /**
11155      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
11156      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11157      * @param {String} value The data value of the item to select
11158      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11159      * selected item if it is not currently in view (defaults to true)
11160      * @return {Boolean} True if the value matched an item in the list, else false
11161      */
11162     selectByValue : function(v, scrollIntoView){
11163         if(v !== undefined && v !== null){
11164             var r = this.findRecord(this.valueField || this.displayField, v);
11165             if(r){
11166                 this.select(this.store.indexOf(r), scrollIntoView);
11167                 return true;
11168             }
11169         }
11170         return false;
11171     },
11172
11173     /**
11174      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
11175      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11176      * @param {Number} index The zero-based index of the list item to select
11177      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11178      * selected item if it is not currently in view (defaults to true)
11179      */
11180     select : function(index, scrollIntoView){
11181         this.selectedIndex = index;
11182         this.view.select(index);
11183         if(scrollIntoView !== false){
11184             var el = this.view.getNode(index);
11185             if(el){
11186                 //this.innerList.scrollChildIntoView(el, false);
11187                 
11188             }
11189         }
11190     },
11191
11192     // private
11193     selectNext : function(){
11194         var ct = this.store.getCount();
11195         if(ct > 0){
11196             if(this.selectedIndex == -1){
11197                 this.select(0);
11198             }else if(this.selectedIndex < ct-1){
11199                 this.select(this.selectedIndex+1);
11200             }
11201         }
11202     },
11203
11204     // private
11205     selectPrev : function(){
11206         var ct = this.store.getCount();
11207         if(ct > 0){
11208             if(this.selectedIndex == -1){
11209                 this.select(0);
11210             }else if(this.selectedIndex != 0){
11211                 this.select(this.selectedIndex-1);
11212             }
11213         }
11214     },
11215
11216     // private
11217     onKeyUp : function(e){
11218         if(this.editable !== false && !e.isSpecialKey()){
11219             this.lastKey = e.getKey();
11220             this.dqTask.delay(this.queryDelay);
11221         }
11222     },
11223
11224     // private
11225     validateBlur : function(){
11226         return !this.list || !this.list.isVisible();   
11227     },
11228
11229     // private
11230     initQuery : function(){
11231         this.doQuery(this.getRawValue());
11232     },
11233
11234     // private
11235     doForce : function(){
11236         if(this.inputEl().dom.value.length > 0){
11237             this.inputEl().dom.value =
11238                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
11239              
11240         }
11241     },
11242
11243     /**
11244      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
11245      * query allowing the query action to be canceled if needed.
11246      * @param {String} query The SQL query to execute
11247      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
11248      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
11249      * saved in the current store (defaults to false)
11250      */
11251     doQuery : function(q, forceAll){
11252         
11253         if(q === undefined || q === null){
11254             q = '';
11255         }
11256         var qe = {
11257             query: q,
11258             forceAll: forceAll,
11259             combo: this,
11260             cancel:false
11261         };
11262         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
11263             return false;
11264         }
11265         q = qe.query;
11266         
11267         forceAll = qe.forceAll;
11268         if(forceAll === true || (q.length >= this.minChars)){
11269             
11270             this.hasQuery = true;
11271             
11272             if(this.lastQuery != q || this.alwaysQuery){
11273                 this.lastQuery = q;
11274                 if(this.mode == 'local'){
11275                     this.selectedIndex = -1;
11276                     if(forceAll){
11277                         this.store.clearFilter();
11278                     }else{
11279                         this.store.filter(this.displayField, q);
11280                     }
11281                     this.onLoad();
11282                 }else{
11283                     this.store.baseParams[this.queryParam] = q;
11284                     
11285                     var options = {params : this.getParams(q)};
11286                     
11287                     if(this.loadNext){
11288                         options.add = true;
11289                         options.params.start = this.page * this.pageSize;
11290                     }
11291                     
11292                     this.store.load(options);
11293                     /*
11294                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
11295                      *  we should expand the list on onLoad
11296                      *  so command out it
11297                      */
11298 //                    this.expand();
11299                 }
11300             }else{
11301                 this.selectedIndex = -1;
11302                 this.onLoad();   
11303             }
11304         }
11305         
11306         this.loadNext = false;
11307     },
11308
11309     // private
11310     getParams : function(q){
11311         var p = {};
11312         //p[this.queryParam] = q;
11313         
11314         if(this.pageSize){
11315             p.start = 0;
11316             p.limit = this.pageSize;
11317         }
11318         return p;
11319     },
11320
11321     /**
11322      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
11323      */
11324     collapse : function(){
11325         if(!this.isExpanded()){
11326             return;
11327         }
11328         
11329         this.hasFocus = false;
11330         
11331         this.list.hide();
11332         
11333         if(this.tickable){
11334             this.okBtn.hide();
11335             this.cancelBtn.hide();
11336             this.trigger.show();
11337         }
11338         
11339         Roo.get(document).un('mousedown', this.collapseIf, this);
11340         Roo.get(document).un('mousewheel', this.collapseIf, this);
11341         if (!this.editable) {
11342             Roo.get(document).un('keydown', this.listKeyPress, this);
11343         }
11344         this.fireEvent('collapse', this);
11345     },
11346
11347     // private
11348     collapseIf : function(e){
11349         var in_combo  = e.within(this.el);
11350         var in_list =  e.within(this.list);
11351         
11352         if (in_combo || in_list) {
11353             //e.stopPropagation();
11354             return;
11355         }
11356         
11357         if(this.tickable){
11358             this.onTickableFooterButtonClick(e, false, false);
11359         }
11360
11361         this.collapse();
11362         
11363     },
11364
11365     /**
11366      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
11367      */
11368     expand : function(){
11369        
11370         if(this.isExpanded() || !this.hasFocus){
11371             return;
11372         }
11373          Roo.log('expand');
11374         this.list.alignTo(this.inputEl(), this.listAlign);
11375         this.list.show();
11376         
11377         if(this.tickable){
11378             
11379             this.tickItems = Roo.apply([], this.item);
11380             
11381             this.okBtn.show();
11382             this.cancelBtn.show();
11383             this.trigger.hide();
11384             
11385         }
11386         
11387         Roo.get(document).on('mousedown', this.collapseIf, this);
11388         Roo.get(document).on('mousewheel', this.collapseIf, this);
11389         if (!this.editable) {
11390             Roo.get(document).on('keydown', this.listKeyPress, this);
11391         }
11392         
11393         this.fireEvent('expand', this);
11394     },
11395
11396     // private
11397     // Implements the default empty TriggerField.onTriggerClick function
11398     onTriggerClick : function(e)
11399     {
11400         Roo.log('trigger click');
11401         
11402         if(this.disabled){
11403             return;
11404         }
11405         
11406         this.page = 0;
11407         this.loadNext = false;
11408         
11409         if(this.isExpanded()){
11410             this.collapse();
11411             if (!this.blockFocus) {
11412                 this.inputEl().focus();
11413             }
11414             
11415         }else {
11416             this.hasFocus = true;
11417             if(this.triggerAction == 'all') {
11418                 this.doQuery(this.allQuery, true);
11419             } else {
11420                 this.doQuery(this.getRawValue());
11421             }
11422             if (!this.blockFocus) {
11423                 this.inputEl().focus();
11424             }
11425         }
11426     },
11427     
11428     onTickableTriggerClick : function(e)
11429     {
11430         if(this.disabled){
11431             return;
11432         }
11433         
11434         this.page = 0;
11435         this.loadNext = false;
11436         this.hasFocus = true;
11437         
11438         if(this.triggerAction == 'all') {
11439             this.doQuery(this.allQuery, true);
11440         } else {
11441             this.doQuery(this.getRawValue());
11442         }
11443     },
11444     
11445     onSearchFieldClick : function(e)
11446     {
11447         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
11448             return;
11449         }
11450         
11451         this.page = 0;
11452         this.loadNext = false;
11453         this.hasFocus = true;
11454         
11455         if(this.triggerAction == 'all') {
11456             this.doQuery(this.allQuery, true);
11457         } else {
11458             this.doQuery(this.getRawValue());
11459         }
11460     },
11461     
11462     listKeyPress : function(e)
11463     {
11464         //Roo.log('listkeypress');
11465         // scroll to first matching element based on key pres..
11466         if (e.isSpecialKey()) {
11467             return false;
11468         }
11469         var k = String.fromCharCode(e.getKey()).toUpperCase();
11470         //Roo.log(k);
11471         var match  = false;
11472         var csel = this.view.getSelectedNodes();
11473         var cselitem = false;
11474         if (csel.length) {
11475             var ix = this.view.indexOf(csel[0]);
11476             cselitem  = this.store.getAt(ix);
11477             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
11478                 cselitem = false;
11479             }
11480             
11481         }
11482         
11483         this.store.each(function(v) { 
11484             if (cselitem) {
11485                 // start at existing selection.
11486                 if (cselitem.id == v.id) {
11487                     cselitem = false;
11488                 }
11489                 return true;
11490             }
11491                 
11492             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
11493                 match = this.store.indexOf(v);
11494                 return false;
11495             }
11496             return true;
11497         }, this);
11498         
11499         if (match === false) {
11500             return true; // no more action?
11501         }
11502         // scroll to?
11503         this.view.select(match);
11504         var sn = Roo.get(this.view.getSelectedNodes()[0])
11505         //sn.scrollIntoView(sn.dom.parentNode, false);
11506     },
11507     
11508     onViewScroll : function(e, t){
11509         
11510         if(this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
11511             return;
11512         }
11513         
11514         this.hasQuery = true;
11515         
11516         this.loading = this.list.select('.loading', true).first();
11517         
11518         if(this.loading === null){
11519             this.list.createChild({
11520                 tag: 'div',
11521                 cls: 'loading select2-more-results select2-active',
11522                 html: 'Loading more results...'
11523             })
11524             
11525             this.loading = this.list.select('.loading', true).first();
11526             
11527             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
11528             
11529             this.loading.hide();
11530         }
11531         
11532         this.loading.show();
11533         
11534         var _combo = this;
11535         
11536         this.page++;
11537         this.loadNext = true;
11538         
11539         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
11540         
11541         return;
11542     },
11543     
11544     addItem : function(o)
11545     {   
11546         var dv = ''; // display value
11547         
11548         if (this.displayField) {
11549             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11550         } else {
11551             // this is an error condition!!!
11552             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
11553         }
11554         
11555         if(!dv.length){
11556             return;
11557         }
11558         
11559         var choice = this.choices.createChild({
11560             tag: 'li',
11561             cls: 'select2-search-choice',
11562             cn: [
11563                 {
11564                     tag: 'div',
11565                     html: dv
11566                 },
11567                 {
11568                     tag: 'a',
11569                     href: '#',
11570                     cls: 'select2-search-choice-close',
11571                     tabindex: '-1'
11572                 }
11573             ]
11574             
11575         }, this.searchField);
11576         
11577         var close = choice.select('a.select2-search-choice-close', true).first()
11578         
11579         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
11580         
11581         this.item.push(o);
11582         
11583         this.lastData = o;
11584         
11585         this.syncValue();
11586         
11587         this.inputEl().dom.value = '';
11588         
11589     },
11590     
11591     onRemoveItem : function(e, _self, o)
11592     {
11593         e.preventDefault();
11594         var index = this.item.indexOf(o.data) * 1;
11595         
11596         if( index < 0){
11597             Roo.log('not this item?!');
11598             return;
11599         }
11600         
11601         this.item.splice(index, 1);
11602         o.item.remove();
11603         
11604         this.syncValue();
11605         
11606         this.fireEvent('remove', this, e);
11607         
11608     },
11609     
11610     syncValue : function()
11611     {
11612         if(!this.item.length){
11613             this.clearValue();
11614             return;
11615         }
11616             
11617         var value = [];
11618         var _this = this;
11619         Roo.each(this.item, function(i){
11620             if(_this.valueField){
11621                 value.push(i[_this.valueField]);
11622                 return;
11623             }
11624
11625             value.push(i);
11626         });
11627
11628         this.value = value.join(',');
11629
11630         if(this.hiddenField){
11631             this.hiddenField.dom.value = this.value;
11632         }
11633     },
11634     
11635     clearItem : function()
11636     {
11637         if(!this.multiple){
11638             return;
11639         }
11640         
11641         this.item = [];
11642         
11643         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
11644            c.remove();
11645         });
11646         
11647         this.syncValue();
11648     },
11649     
11650     inputEl: function ()
11651     {
11652         if(this.tickable){
11653             return this.searchField;
11654         }
11655         return this.el.select('input.form-control',true).first();
11656     },
11657     
11658     
11659     onTickableFooterButtonClick : function(e, btn, el)
11660     {
11661         e.preventDefault();
11662         
11663         if(btn && btn.name == 'cancel'){
11664             this.tickItems = Roo.apply([], this.item);
11665             this.collapse();
11666             return;
11667         }
11668         
11669         this.clearItem();
11670         
11671         var _this = this;
11672         
11673         Roo.each(this.tickItems, function(o){
11674             _this.addItem(o);
11675         });
11676         
11677         this.collapse();
11678         
11679     }
11680     
11681     
11682
11683     /** 
11684     * @cfg {Boolean} grow 
11685     * @hide 
11686     */
11687     /** 
11688     * @cfg {Number} growMin 
11689     * @hide 
11690     */
11691     /** 
11692     * @cfg {Number} growMax 
11693     * @hide 
11694     */
11695     /**
11696      * @hide
11697      * @method autoSize
11698      */
11699 });
11700 /*
11701  * Based on:
11702  * Ext JS Library 1.1.1
11703  * Copyright(c) 2006-2007, Ext JS, LLC.
11704  *
11705  * Originally Released Under LGPL - original licence link has changed is not relivant.
11706  *
11707  * Fork - LGPL
11708  * <script type="text/javascript">
11709  */
11710
11711 /**
11712  * @class Roo.View
11713  * @extends Roo.util.Observable
11714  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
11715  * This class also supports single and multi selection modes. <br>
11716  * Create a data model bound view:
11717  <pre><code>
11718  var store = new Roo.data.Store(...);
11719
11720  var view = new Roo.View({
11721     el : "my-element",
11722     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
11723  
11724     singleSelect: true,
11725     selectedClass: "ydataview-selected",
11726     store: store
11727  });
11728
11729  // listen for node click?
11730  view.on("click", function(vw, index, node, e){
11731  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
11732  });
11733
11734  // load XML data
11735  dataModel.load("foobar.xml");
11736  </code></pre>
11737  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
11738  * <br><br>
11739  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
11740  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
11741  * 
11742  * Note: old style constructor is still suported (container, template, config)
11743  * 
11744  * @constructor
11745  * Create a new View
11746  * @param {Object} config The config object
11747  * 
11748  */
11749 Roo.View = function(config, depreciated_tpl, depreciated_config){
11750     
11751     this.parent = false;
11752     
11753     if (typeof(depreciated_tpl) == 'undefined') {
11754         // new way.. - universal constructor.
11755         Roo.apply(this, config);
11756         this.el  = Roo.get(this.el);
11757     } else {
11758         // old format..
11759         this.el  = Roo.get(config);
11760         this.tpl = depreciated_tpl;
11761         Roo.apply(this, depreciated_config);
11762     }
11763     this.wrapEl  = this.el.wrap().wrap();
11764     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
11765     
11766     
11767     if(typeof(this.tpl) == "string"){
11768         this.tpl = new Roo.Template(this.tpl);
11769     } else {
11770         // support xtype ctors..
11771         this.tpl = new Roo.factory(this.tpl, Roo);
11772     }
11773     
11774     
11775     this.tpl.compile();
11776     
11777     /** @private */
11778     this.addEvents({
11779         /**
11780          * @event beforeclick
11781          * Fires before a click is processed. Returns false to cancel the default action.
11782          * @param {Roo.View} this
11783          * @param {Number} index The index of the target node
11784          * @param {HTMLElement} node The target node
11785          * @param {Roo.EventObject} e The raw event object
11786          */
11787             "beforeclick" : true,
11788         /**
11789          * @event click
11790          * Fires when a template node is clicked.
11791          * @param {Roo.View} this
11792          * @param {Number} index The index of the target node
11793          * @param {HTMLElement} node The target node
11794          * @param {Roo.EventObject} e The raw event object
11795          */
11796             "click" : true,
11797         /**
11798          * @event dblclick
11799          * Fires when a template node is double clicked.
11800          * @param {Roo.View} this
11801          * @param {Number} index The index of the target node
11802          * @param {HTMLElement} node The target node
11803          * @param {Roo.EventObject} e The raw event object
11804          */
11805             "dblclick" : true,
11806         /**
11807          * @event contextmenu
11808          * Fires when a template node is right clicked.
11809          * @param {Roo.View} this
11810          * @param {Number} index The index of the target node
11811          * @param {HTMLElement} node The target node
11812          * @param {Roo.EventObject} e The raw event object
11813          */
11814             "contextmenu" : true,
11815         /**
11816          * @event selectionchange
11817          * Fires when the selected nodes change.
11818          * @param {Roo.View} this
11819          * @param {Array} selections Array of the selected nodes
11820          */
11821             "selectionchange" : true,
11822     
11823         /**
11824          * @event beforeselect
11825          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
11826          * @param {Roo.View} this
11827          * @param {HTMLElement} node The node to be selected
11828          * @param {Array} selections Array of currently selected nodes
11829          */
11830             "beforeselect" : true,
11831         /**
11832          * @event preparedata
11833          * Fires on every row to render, to allow you to change the data.
11834          * @param {Roo.View} this
11835          * @param {Object} data to be rendered (change this)
11836          */
11837           "preparedata" : true
11838           
11839           
11840         });
11841
11842
11843
11844     this.el.on({
11845         "click": this.onClick,
11846         "dblclick": this.onDblClick,
11847         "contextmenu": this.onContextMenu,
11848         scope:this
11849     });
11850
11851     this.selections = [];
11852     this.nodes = [];
11853     this.cmp = new Roo.CompositeElementLite([]);
11854     if(this.store){
11855         this.store = Roo.factory(this.store, Roo.data);
11856         this.setStore(this.store, true);
11857     }
11858     
11859     if ( this.footer && this.footer.xtype) {
11860            
11861          var fctr = this.wrapEl.appendChild(document.createElement("div"));
11862         
11863         this.footer.dataSource = this.store
11864         this.footer.container = fctr;
11865         this.footer = Roo.factory(this.footer, Roo);
11866         fctr.insertFirst(this.el);
11867         
11868         // this is a bit insane - as the paging toolbar seems to detach the el..
11869 //        dom.parentNode.parentNode.parentNode
11870          // they get detached?
11871     }
11872     
11873     
11874     Roo.View.superclass.constructor.call(this);
11875     
11876     
11877 };
11878
11879 Roo.extend(Roo.View, Roo.util.Observable, {
11880     
11881      /**
11882      * @cfg {Roo.data.Store} store Data store to load data from.
11883      */
11884     store : false,
11885     
11886     /**
11887      * @cfg {String|Roo.Element} el The container element.
11888      */
11889     el : '',
11890     
11891     /**
11892      * @cfg {String|Roo.Template} tpl The template used by this View 
11893      */
11894     tpl : false,
11895     /**
11896      * @cfg {String} dataName the named area of the template to use as the data area
11897      *                          Works with domtemplates roo-name="name"
11898      */
11899     dataName: false,
11900     /**
11901      * @cfg {String} selectedClass The css class to add to selected nodes
11902      */
11903     selectedClass : "x-view-selected",
11904      /**
11905      * @cfg {String} emptyText The empty text to show when nothing is loaded.
11906      */
11907     emptyText : "",
11908     
11909     /**
11910      * @cfg {String} text to display on mask (default Loading)
11911      */
11912     mask : false,
11913     /**
11914      * @cfg {Boolean} multiSelect Allow multiple selection
11915      */
11916     multiSelect : false,
11917     /**
11918      * @cfg {Boolean} singleSelect Allow single selection
11919      */
11920     singleSelect:  false,
11921     
11922     /**
11923      * @cfg {Boolean} toggleSelect - selecting 
11924      */
11925     toggleSelect : false,
11926     
11927     /**
11928      * @cfg {Boolean} tickable - selecting 
11929      */
11930     tickable : false,
11931     
11932     /**
11933      * Returns the element this view is bound to.
11934      * @return {Roo.Element}
11935      */
11936     getEl : function(){
11937         return this.wrapEl;
11938     },
11939     
11940     
11941
11942     /**
11943      * Refreshes the view. - called by datachanged on the store. - do not call directly.
11944      */
11945     refresh : function(){
11946         Roo.log('refresh');
11947         var t = this.tpl;
11948         
11949         // if we are using something like 'domtemplate', then
11950         // the what gets used is:
11951         // t.applySubtemplate(NAME, data, wrapping data..)
11952         // the outer template then get' applied with
11953         //     the store 'extra data'
11954         // and the body get's added to the
11955         //      roo-name="data" node?
11956         //      <span class='roo-tpl-{name}'></span> ?????
11957         
11958         
11959         
11960         this.clearSelections();
11961         this.el.update("");
11962         var html = [];
11963         var records = this.store.getRange();
11964         if(records.length < 1) {
11965             
11966             // is this valid??  = should it render a template??
11967             
11968             this.el.update(this.emptyText);
11969             return;
11970         }
11971         var el = this.el;
11972         if (this.dataName) {
11973             this.el.update(t.apply(this.store.meta)); //????
11974             el = this.el.child('.roo-tpl-' + this.dataName);
11975         }
11976         
11977         for(var i = 0, len = records.length; i < len; i++){
11978             var data = this.prepareData(records[i].data, i, records[i]);
11979             this.fireEvent("preparedata", this, data, i, records[i]);
11980             
11981             var d = Roo.apply({}, data);
11982             
11983             if(this.tickable){
11984                 Roo.apply(d, {'roo-id' : Roo.id()});
11985                 
11986                 var _this = this;
11987             
11988                 Roo.each(this.parent.item, function(item){
11989                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
11990                         return;
11991                     }
11992                     Roo.apply(d, {'roo-data-checked' : 'checked'});
11993                 });
11994             }
11995             
11996             html[html.length] = Roo.util.Format.trim(
11997                 this.dataName ?
11998                     t.applySubtemplate(this.dataName, d, this.store.meta) :
11999                     t.apply(d)
12000             );
12001         }
12002         
12003         
12004         
12005         el.update(html.join(""));
12006         this.nodes = el.dom.childNodes;
12007         this.updateIndexes(0);
12008     },
12009     
12010
12011     /**
12012      * Function to override to reformat the data that is sent to
12013      * the template for each node.
12014      * DEPRICATED - use the preparedata event handler.
12015      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
12016      * a JSON object for an UpdateManager bound view).
12017      */
12018     prepareData : function(data, index, record)
12019     {
12020         this.fireEvent("preparedata", this, data, index, record);
12021         return data;
12022     },
12023
12024     onUpdate : function(ds, record){
12025          Roo.log('on update');   
12026         this.clearSelections();
12027         var index = this.store.indexOf(record);
12028         var n = this.nodes[index];
12029         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
12030         n.parentNode.removeChild(n);
12031         this.updateIndexes(index, index);
12032     },
12033
12034     
12035     
12036 // --------- FIXME     
12037     onAdd : function(ds, records, index)
12038     {
12039         Roo.log(['on Add', ds, records, index] );        
12040         this.clearSelections();
12041         if(this.nodes.length == 0){
12042             this.refresh();
12043             return;
12044         }
12045         var n = this.nodes[index];
12046         for(var i = 0, len = records.length; i < len; i++){
12047             var d = this.prepareData(records[i].data, i, records[i]);
12048             if(n){
12049                 this.tpl.insertBefore(n, d);
12050             }else{
12051                 
12052                 this.tpl.append(this.el, d);
12053             }
12054         }
12055         this.updateIndexes(index);
12056     },
12057
12058     onRemove : function(ds, record, index){
12059         Roo.log('onRemove');
12060         this.clearSelections();
12061         var el = this.dataName  ?
12062             this.el.child('.roo-tpl-' + this.dataName) :
12063             this.el; 
12064         
12065         el.dom.removeChild(this.nodes[index]);
12066         this.updateIndexes(index);
12067     },
12068
12069     /**
12070      * Refresh an individual node.
12071      * @param {Number} index
12072      */
12073     refreshNode : function(index){
12074         this.onUpdate(this.store, this.store.getAt(index));
12075     },
12076
12077     updateIndexes : function(startIndex, endIndex){
12078         var ns = this.nodes;
12079         startIndex = startIndex || 0;
12080         endIndex = endIndex || ns.length - 1;
12081         for(var i = startIndex; i <= endIndex; i++){
12082             ns[i].nodeIndex = i;
12083         }
12084     },
12085
12086     /**
12087      * Changes the data store this view uses and refresh the view.
12088      * @param {Store} store
12089      */
12090     setStore : function(store, initial){
12091         if(!initial && this.store){
12092             this.store.un("datachanged", this.refresh);
12093             this.store.un("add", this.onAdd);
12094             this.store.un("remove", this.onRemove);
12095             this.store.un("update", this.onUpdate);
12096             this.store.un("clear", this.refresh);
12097             this.store.un("beforeload", this.onBeforeLoad);
12098             this.store.un("load", this.onLoad);
12099             this.store.un("loadexception", this.onLoad);
12100         }
12101         if(store){
12102           
12103             store.on("datachanged", this.refresh, this);
12104             store.on("add", this.onAdd, this);
12105             store.on("remove", this.onRemove, this);
12106             store.on("update", this.onUpdate, this);
12107             store.on("clear", this.refresh, this);
12108             store.on("beforeload", this.onBeforeLoad, this);
12109             store.on("load", this.onLoad, this);
12110             store.on("loadexception", this.onLoad, this);
12111         }
12112         
12113         if(store){
12114             this.refresh();
12115         }
12116     },
12117     /**
12118      * onbeforeLoad - masks the loading area.
12119      *
12120      */
12121     onBeforeLoad : function(store,opts)
12122     {
12123          Roo.log('onBeforeLoad');   
12124         if (!opts.add) {
12125             this.el.update("");
12126         }
12127         this.el.mask(this.mask ? this.mask : "Loading" ); 
12128     },
12129     onLoad : function ()
12130     {
12131         this.el.unmask();
12132     },
12133     
12134
12135     /**
12136      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
12137      * @param {HTMLElement} node
12138      * @return {HTMLElement} The template node
12139      */
12140     findItemFromChild : function(node){
12141         var el = this.dataName  ?
12142             this.el.child('.roo-tpl-' + this.dataName,true) :
12143             this.el.dom; 
12144         
12145         if(!node || node.parentNode == el){
12146                     return node;
12147             }
12148             var p = node.parentNode;
12149             while(p && p != el){
12150             if(p.parentNode == el){
12151                 return p;
12152             }
12153             p = p.parentNode;
12154         }
12155             return null;
12156     },
12157
12158     /** @ignore */
12159     onClick : function(e){
12160         var item = this.findItemFromChild(e.getTarget());
12161         if(item){
12162             var index = this.indexOf(item);
12163             if(this.onItemClick(item, index, e) !== false){
12164                 this.fireEvent("click", this, index, item, e);
12165             }
12166         }else{
12167             this.clearSelections();
12168         }
12169     },
12170
12171     /** @ignore */
12172     onContextMenu : function(e){
12173         var item = this.findItemFromChild(e.getTarget());
12174         if(item){
12175             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
12176         }
12177     },
12178
12179     /** @ignore */
12180     onDblClick : function(e){
12181         var item = this.findItemFromChild(e.getTarget());
12182         if(item){
12183             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
12184         }
12185     },
12186
12187     onItemClick : function(item, index, e)
12188     {
12189         if(this.fireEvent("beforeclick", this, index, item, e) === false){
12190             return false;
12191         }
12192         if (this.toggleSelect) {
12193             var m = this.isSelected(item) ? 'unselect' : 'select';
12194             Roo.log(m);
12195             var _t = this;
12196             _t[m](item, true, false);
12197             return true;
12198         }
12199         if(this.multiSelect || this.singleSelect){
12200             if(this.multiSelect && e.shiftKey && this.lastSelection){
12201                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
12202             }else{
12203                 this.select(item, this.multiSelect && e.ctrlKey);
12204                 this.lastSelection = item;
12205             }
12206             
12207             if(!this.tickable){
12208                 e.preventDefault();
12209             }
12210             
12211         }
12212         return true;
12213     },
12214
12215     /**
12216      * Get the number of selected nodes.
12217      * @return {Number}
12218      */
12219     getSelectionCount : function(){
12220         return this.selections.length;
12221     },
12222
12223     /**
12224      * Get the currently selected nodes.
12225      * @return {Array} An array of HTMLElements
12226      */
12227     getSelectedNodes : function(){
12228         return this.selections;
12229     },
12230
12231     /**
12232      * Get the indexes of the selected nodes.
12233      * @return {Array}
12234      */
12235     getSelectedIndexes : function(){
12236         var indexes = [], s = this.selections;
12237         for(var i = 0, len = s.length; i < len; i++){
12238             indexes.push(s[i].nodeIndex);
12239         }
12240         return indexes;
12241     },
12242
12243     /**
12244      * Clear all selections
12245      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
12246      */
12247     clearSelections : function(suppressEvent){
12248         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
12249             this.cmp.elements = this.selections;
12250             this.cmp.removeClass(this.selectedClass);
12251             this.selections = [];
12252             if(!suppressEvent){
12253                 this.fireEvent("selectionchange", this, this.selections);
12254             }
12255         }
12256     },
12257
12258     /**
12259      * Returns true if the passed node is selected
12260      * @param {HTMLElement/Number} node The node or node index
12261      * @return {Boolean}
12262      */
12263     isSelected : function(node){
12264         var s = this.selections;
12265         if(s.length < 1){
12266             return false;
12267         }
12268         node = this.getNode(node);
12269         return s.indexOf(node) !== -1;
12270     },
12271
12272     /**
12273      * Selects nodes.
12274      * @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
12275      * @param {Boolean} keepExisting (optional) true to keep existing selections
12276      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12277      */
12278     select : function(nodeInfo, keepExisting, suppressEvent){
12279         if(nodeInfo instanceof Array){
12280             if(!keepExisting){
12281                 this.clearSelections(true);
12282             }
12283             for(var i = 0, len = nodeInfo.length; i < len; i++){
12284                 this.select(nodeInfo[i], true, true);
12285             }
12286             return;
12287         } 
12288         var node = this.getNode(nodeInfo);
12289         if(!node || this.isSelected(node)){
12290             return; // already selected.
12291         }
12292         if(!keepExisting){
12293             this.clearSelections(true);
12294         }
12295         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
12296             Roo.fly(node).addClass(this.selectedClass);
12297             this.selections.push(node);
12298             if(!suppressEvent){
12299                 this.fireEvent("selectionchange", this, this.selections);
12300             }
12301         }
12302         
12303         
12304     },
12305       /**
12306      * Unselects nodes.
12307      * @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
12308      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
12309      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12310      */
12311     unselect : function(nodeInfo, keepExisting, suppressEvent)
12312     {
12313         if(nodeInfo instanceof Array){
12314             Roo.each(this.selections, function(s) {
12315                 this.unselect(s, nodeInfo);
12316             }, this);
12317             return;
12318         }
12319         var node = this.getNode(nodeInfo);
12320         if(!node || !this.isSelected(node)){
12321             Roo.log("not selected");
12322             return; // not selected.
12323         }
12324         // fireevent???
12325         var ns = [];
12326         Roo.each(this.selections, function(s) {
12327             if (s == node ) {
12328                 Roo.fly(node).removeClass(this.selectedClass);
12329
12330                 return;
12331             }
12332             ns.push(s);
12333         },this);
12334         
12335         this.selections= ns;
12336         this.fireEvent("selectionchange", this, this.selections);
12337     },
12338
12339     /**
12340      * Gets a template node.
12341      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12342      * @return {HTMLElement} The node or null if it wasn't found
12343      */
12344     getNode : function(nodeInfo){
12345         if(typeof nodeInfo == "string"){
12346             return document.getElementById(nodeInfo);
12347         }else if(typeof nodeInfo == "number"){
12348             return this.nodes[nodeInfo];
12349         }
12350         return nodeInfo;
12351     },
12352
12353     /**
12354      * Gets a range template nodes.
12355      * @param {Number} startIndex
12356      * @param {Number} endIndex
12357      * @return {Array} An array of nodes
12358      */
12359     getNodes : function(start, end){
12360         var ns = this.nodes;
12361         start = start || 0;
12362         end = typeof end == "undefined" ? ns.length - 1 : end;
12363         var nodes = [];
12364         if(start <= end){
12365             for(var i = start; i <= end; i++){
12366                 nodes.push(ns[i]);
12367             }
12368         } else{
12369             for(var i = start; i >= end; i--){
12370                 nodes.push(ns[i]);
12371             }
12372         }
12373         return nodes;
12374     },
12375
12376     /**
12377      * Finds the index of the passed node
12378      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12379      * @return {Number} The index of the node or -1
12380      */
12381     indexOf : function(node){
12382         node = this.getNode(node);
12383         if(typeof node.nodeIndex == "number"){
12384             return node.nodeIndex;
12385         }
12386         var ns = this.nodes;
12387         for(var i = 0, len = ns.length; i < len; i++){
12388             if(ns[i] == node){
12389                 return i;
12390             }
12391         }
12392         return -1;
12393     }
12394 });
12395 /*
12396  * - LGPL
12397  *
12398  * based on jquery fullcalendar
12399  * 
12400  */
12401
12402 Roo.bootstrap = Roo.bootstrap || {};
12403 /**
12404  * @class Roo.bootstrap.Calendar
12405  * @extends Roo.bootstrap.Component
12406  * Bootstrap Calendar class
12407  * @cfg {Boolean} loadMask (true|false) default false
12408  * @cfg {Object} header generate the user specific header of the calendar, default false
12409
12410  * @constructor
12411  * Create a new Container
12412  * @param {Object} config The config object
12413  */
12414
12415
12416
12417 Roo.bootstrap.Calendar = function(config){
12418     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
12419      this.addEvents({
12420         /**
12421              * @event select
12422              * Fires when a date is selected
12423              * @param {DatePicker} this
12424              * @param {Date} date The selected date
12425              */
12426         'select': true,
12427         /**
12428              * @event monthchange
12429              * Fires when the displayed month changes 
12430              * @param {DatePicker} this
12431              * @param {Date} date The selected month
12432              */
12433         'monthchange': true,
12434         /**
12435              * @event evententer
12436              * Fires when mouse over an event
12437              * @param {Calendar} this
12438              * @param {event} Event
12439              */
12440         'evententer': true,
12441         /**
12442              * @event eventleave
12443              * Fires when the mouse leaves an
12444              * @param {Calendar} this
12445              * @param {event}
12446              */
12447         'eventleave': true,
12448         /**
12449              * @event eventclick
12450              * Fires when the mouse click an
12451              * @param {Calendar} this
12452              * @param {event}
12453              */
12454         'eventclick': true
12455         
12456     });
12457
12458 };
12459
12460 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
12461     
12462      /**
12463      * @cfg {Number} startDay
12464      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
12465      */
12466     startDay : 0,
12467     
12468     loadMask : false,
12469     
12470     header : false,
12471       
12472     getAutoCreate : function(){
12473         
12474         
12475         var fc_button = function(name, corner, style, content ) {
12476             return Roo.apply({},{
12477                 tag : 'span',
12478                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
12479                          (corner.length ?
12480                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
12481                             ''
12482                         ),
12483                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
12484                 unselectable: 'on'
12485             });
12486         };
12487         
12488         var header = {};
12489         
12490         if(!this.header){
12491             header = {
12492                 tag : 'table',
12493                 cls : 'fc-header',
12494                 style : 'width:100%',
12495                 cn : [
12496                     {
12497                         tag: 'tr',
12498                         cn : [
12499                             {
12500                                 tag : 'td',
12501                                 cls : 'fc-header-left',
12502                                 cn : [
12503                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
12504                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
12505                                     { tag: 'span', cls: 'fc-header-space' },
12506                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
12507
12508
12509                                 ]
12510                             },
12511
12512                             {
12513                                 tag : 'td',
12514                                 cls : 'fc-header-center',
12515                                 cn : [
12516                                     {
12517                                         tag: 'span',
12518                                         cls: 'fc-header-title',
12519                                         cn : {
12520                                             tag: 'H2',
12521                                             html : 'month / year'
12522                                         }
12523                                     }
12524
12525                                 ]
12526                             },
12527                             {
12528                                 tag : 'td',
12529                                 cls : 'fc-header-right',
12530                                 cn : [
12531                               /*      fc_button('month', 'left', '', 'month' ),
12532                                     fc_button('week', '', '', 'week' ),
12533                                     fc_button('day', 'right', '', 'day' )
12534                                 */    
12535
12536                                 ]
12537                             }
12538
12539                         ]
12540                     }
12541                 ]
12542             };
12543         }
12544         
12545         header = this.header;
12546         
12547        
12548         var cal_heads = function() {
12549             var ret = [];
12550             // fixme - handle this.
12551             
12552             for (var i =0; i < Date.dayNames.length; i++) {
12553                 var d = Date.dayNames[i];
12554                 ret.push({
12555                     tag: 'th',
12556                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
12557                     html : d.substring(0,3)
12558                 });
12559                 
12560             }
12561             ret[0].cls += ' fc-first';
12562             ret[6].cls += ' fc-last';
12563             return ret;
12564         };
12565         var cal_cell = function(n) {
12566             return  {
12567                 tag: 'td',
12568                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
12569                 cn : [
12570                     {
12571                         cn : [
12572                             {
12573                                 cls: 'fc-day-number',
12574                                 html: 'D'
12575                             },
12576                             {
12577                                 cls: 'fc-day-content',
12578                              
12579                                 cn : [
12580                                      {
12581                                         style: 'position: relative;' // height: 17px;
12582                                     }
12583                                 ]
12584                             }
12585                             
12586                             
12587                         ]
12588                     }
12589                 ]
12590                 
12591             }
12592         };
12593         var cal_rows = function() {
12594             
12595             var ret = []
12596             for (var r = 0; r < 6; r++) {
12597                 var row= {
12598                     tag : 'tr',
12599                     cls : 'fc-week',
12600                     cn : []
12601                 };
12602                 
12603                 for (var i =0; i < Date.dayNames.length; i++) {
12604                     var d = Date.dayNames[i];
12605                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
12606
12607                 }
12608                 row.cn[0].cls+=' fc-first';
12609                 row.cn[0].cn[0].style = 'min-height:90px';
12610                 row.cn[6].cls+=' fc-last';
12611                 ret.push(row);
12612                 
12613             }
12614             ret[0].cls += ' fc-first';
12615             ret[4].cls += ' fc-prev-last';
12616             ret[5].cls += ' fc-last';
12617             return ret;
12618             
12619         };
12620         
12621         var cal_table = {
12622             tag: 'table',
12623             cls: 'fc-border-separate',
12624             style : 'width:100%',
12625             cellspacing  : 0,
12626             cn : [
12627                 { 
12628                     tag: 'thead',
12629                     cn : [
12630                         { 
12631                             tag: 'tr',
12632                             cls : 'fc-first fc-last',
12633                             cn : cal_heads()
12634                         }
12635                     ]
12636                 },
12637                 { 
12638                     tag: 'tbody',
12639                     cn : cal_rows()
12640                 }
12641                   
12642             ]
12643         };
12644          
12645          var cfg = {
12646             cls : 'fc fc-ltr',
12647             cn : [
12648                 header,
12649                 {
12650                     cls : 'fc-content',
12651                     style : "position: relative;",
12652                     cn : [
12653                         {
12654                             cls : 'fc-view fc-view-month fc-grid',
12655                             style : 'position: relative',
12656                             unselectable : 'on',
12657                             cn : [
12658                                 {
12659                                     cls : 'fc-event-container',
12660                                     style : 'position:absolute;z-index:8;top:0;left:0;'
12661                                 },
12662                                 cal_table
12663                             ]
12664                         }
12665                     ]
12666     
12667                 }
12668            ] 
12669             
12670         };
12671         
12672          
12673         
12674         return cfg;
12675     },
12676     
12677     
12678     initEvents : function()
12679     {
12680         if(!this.store){
12681             throw "can not find store for calendar";
12682         }
12683         
12684         var mark = {
12685             tag: "div",
12686             cls:"x-dlg-mask",
12687             style: "text-align:center",
12688             cn: [
12689                 {
12690                     tag: "div",
12691                     style: "background-color:white;width:50%;margin:250 auto",
12692                     cn: [
12693                         {
12694                             tag: "img",
12695                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
12696                         },
12697                         {
12698                             tag: "span",
12699                             html: "Loading"
12700                         }
12701                         
12702                     ]
12703                 }
12704             ]
12705         }
12706         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
12707         
12708         var size = this.el.select('.fc-content', true).first().getSize();
12709         this.maskEl.setSize(size.width, size.height);
12710         this.maskEl.enableDisplayMode("block");
12711         if(!this.loadMask){
12712             this.maskEl.hide();
12713         }
12714         
12715         this.store = Roo.factory(this.store, Roo.data);
12716         this.store.on('load', this.onLoad, this);
12717         this.store.on('beforeload', this.onBeforeLoad, this);
12718         
12719         this.resize();
12720         
12721         this.cells = this.el.select('.fc-day',true);
12722         //Roo.log(this.cells);
12723         this.textNodes = this.el.query('.fc-day-number');
12724         this.cells.addClassOnOver('fc-state-hover');
12725         
12726         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
12727         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
12728         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
12729         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
12730         
12731         this.on('monthchange', this.onMonthChange, this);
12732         
12733         this.update(new Date().clearTime());
12734     },
12735     
12736     resize : function() {
12737         var sz  = this.el.getSize();
12738         
12739         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
12740         this.el.select('.fc-day-content div',true).setHeight(34);
12741     },
12742     
12743     
12744     // private
12745     showPrevMonth : function(e){
12746         this.update(this.activeDate.add("mo", -1));
12747     },
12748     showToday : function(e){
12749         this.update(new Date().clearTime());
12750     },
12751     // private
12752     showNextMonth : function(e){
12753         this.update(this.activeDate.add("mo", 1));
12754     },
12755
12756     // private
12757     showPrevYear : function(){
12758         this.update(this.activeDate.add("y", -1));
12759     },
12760
12761     // private
12762     showNextYear : function(){
12763         this.update(this.activeDate.add("y", 1));
12764     },
12765
12766     
12767    // private
12768     update : function(date)
12769     {
12770         var vd = this.activeDate;
12771         this.activeDate = date;
12772 //        if(vd && this.el){
12773 //            var t = date.getTime();
12774 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
12775 //                Roo.log('using add remove');
12776 //                
12777 //                this.fireEvent('monthchange', this, date);
12778 //                
12779 //                this.cells.removeClass("fc-state-highlight");
12780 //                this.cells.each(function(c){
12781 //                   if(c.dateValue == t){
12782 //                       c.addClass("fc-state-highlight");
12783 //                       setTimeout(function(){
12784 //                            try{c.dom.firstChild.focus();}catch(e){}
12785 //                       }, 50);
12786 //                       return false;
12787 //                   }
12788 //                   return true;
12789 //                });
12790 //                return;
12791 //            }
12792 //        }
12793         
12794         var days = date.getDaysInMonth();
12795         
12796         var firstOfMonth = date.getFirstDateOfMonth();
12797         var startingPos = firstOfMonth.getDay()-this.startDay;
12798         
12799         if(startingPos < this.startDay){
12800             startingPos += 7;
12801         }
12802         
12803         var pm = date.add(Date.MONTH, -1);
12804         var prevStart = pm.getDaysInMonth()-startingPos;
12805 //        
12806         this.cells = this.el.select('.fc-day',true);
12807         this.textNodes = this.el.query('.fc-day-number');
12808         this.cells.addClassOnOver('fc-state-hover');
12809         
12810         var cells = this.cells.elements;
12811         var textEls = this.textNodes;
12812         
12813         Roo.each(cells, function(cell){
12814             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
12815         });
12816         
12817         days += startingPos;
12818
12819         // convert everything to numbers so it's fast
12820         var day = 86400000;
12821         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
12822         //Roo.log(d);
12823         //Roo.log(pm);
12824         //Roo.log(prevStart);
12825         
12826         var today = new Date().clearTime().getTime();
12827         var sel = date.clearTime().getTime();
12828         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
12829         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
12830         var ddMatch = this.disabledDatesRE;
12831         var ddText = this.disabledDatesText;
12832         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
12833         var ddaysText = this.disabledDaysText;
12834         var format = this.format;
12835         
12836         var setCellClass = function(cal, cell){
12837             cell.row = 0;
12838             cell.events = [];
12839             cell.more = [];
12840             //Roo.log('set Cell Class');
12841             cell.title = "";
12842             var t = d.getTime();
12843             
12844             //Roo.log(d);
12845             
12846             cell.dateValue = t;
12847             if(t == today){
12848                 cell.className += " fc-today";
12849                 cell.className += " fc-state-highlight";
12850                 cell.title = cal.todayText;
12851             }
12852             if(t == sel){
12853                 // disable highlight in other month..
12854                 //cell.className += " fc-state-highlight";
12855                 
12856             }
12857             // disabling
12858             if(t < min) {
12859                 cell.className = " fc-state-disabled";
12860                 cell.title = cal.minText;
12861                 return;
12862             }
12863             if(t > max) {
12864                 cell.className = " fc-state-disabled";
12865                 cell.title = cal.maxText;
12866                 return;
12867             }
12868             if(ddays){
12869                 if(ddays.indexOf(d.getDay()) != -1){
12870                     cell.title = ddaysText;
12871                     cell.className = " fc-state-disabled";
12872                 }
12873             }
12874             if(ddMatch && format){
12875                 var fvalue = d.dateFormat(format);
12876                 if(ddMatch.test(fvalue)){
12877                     cell.title = ddText.replace("%0", fvalue);
12878                     cell.className = " fc-state-disabled";
12879                 }
12880             }
12881             
12882             if (!cell.initialClassName) {
12883                 cell.initialClassName = cell.dom.className;
12884             }
12885             
12886             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
12887         };
12888
12889         var i = 0;
12890         
12891         for(; i < startingPos; i++) {
12892             textEls[i].innerHTML = (++prevStart);
12893             d.setDate(d.getDate()+1);
12894             
12895             cells[i].className = "fc-past fc-other-month";
12896             setCellClass(this, cells[i]);
12897         }
12898         
12899         var intDay = 0;
12900         
12901         for(; i < days; i++){
12902             intDay = i - startingPos + 1;
12903             textEls[i].innerHTML = (intDay);
12904             d.setDate(d.getDate()+1);
12905             
12906             cells[i].className = ''; // "x-date-active";
12907             setCellClass(this, cells[i]);
12908         }
12909         var extraDays = 0;
12910         
12911         for(; i < 42; i++) {
12912             textEls[i].innerHTML = (++extraDays);
12913             d.setDate(d.getDate()+1);
12914             
12915             cells[i].className = "fc-future fc-other-month";
12916             setCellClass(this, cells[i]);
12917         }
12918         
12919         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
12920         
12921         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
12922         
12923         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
12924         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
12925         
12926         if(totalRows != 6){
12927             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
12928             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
12929         }
12930         
12931         this.fireEvent('monthchange', this, date);
12932         
12933         
12934         /*
12935         if(!this.internalRender){
12936             var main = this.el.dom.firstChild;
12937             var w = main.offsetWidth;
12938             this.el.setWidth(w + this.el.getBorderWidth("lr"));
12939             Roo.fly(main).setWidth(w);
12940             this.internalRender = true;
12941             // opera does not respect the auto grow header center column
12942             // then, after it gets a width opera refuses to recalculate
12943             // without a second pass
12944             if(Roo.isOpera && !this.secondPass){
12945                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
12946                 this.secondPass = true;
12947                 this.update.defer(10, this, [date]);
12948             }
12949         }
12950         */
12951         
12952     },
12953     
12954     findCell : function(dt) {
12955         dt = dt.clearTime().getTime();
12956         var ret = false;
12957         this.cells.each(function(c){
12958             //Roo.log("check " +c.dateValue + '?=' + dt);
12959             if(c.dateValue == dt){
12960                 ret = c;
12961                 return false;
12962             }
12963             return true;
12964         });
12965         
12966         return ret;
12967     },
12968     
12969     findCells : function(ev) {
12970         var s = ev.start.clone().clearTime().getTime();
12971        // Roo.log(s);
12972         var e= ev.end.clone().clearTime().getTime();
12973        // Roo.log(e);
12974         var ret = [];
12975         this.cells.each(function(c){
12976              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
12977             
12978             if(c.dateValue > e){
12979                 return ;
12980             }
12981             if(c.dateValue < s){
12982                 return ;
12983             }
12984             ret.push(c);
12985         });
12986         
12987         return ret;    
12988     },
12989     
12990 //    findBestRow: function(cells)
12991 //    {
12992 //        var ret = 0;
12993 //        
12994 //        for (var i =0 ; i < cells.length;i++) {
12995 //            ret  = Math.max(cells[i].rows || 0,ret);
12996 //        }
12997 //        return ret;
12998 //        
12999 //    },
13000     
13001     
13002     addItem : function(ev)
13003     {
13004         // look for vertical location slot in
13005         var cells = this.findCells(ev);
13006         
13007 //        ev.row = this.findBestRow(cells);
13008         
13009         // work out the location.
13010         
13011         var crow = false;
13012         var rows = [];
13013         for(var i =0; i < cells.length; i++) {
13014             
13015             cells[i].row = cells[0].row;
13016             
13017             if(i == 0){
13018                 cells[i].row = cells[i].row + 1;
13019             }
13020             
13021             if (!crow) {
13022                 crow = {
13023                     start : cells[i],
13024                     end :  cells[i]
13025                 };
13026                 continue;
13027             }
13028             if (crow.start.getY() == cells[i].getY()) {
13029                 // on same row.
13030                 crow.end = cells[i];
13031                 continue;
13032             }
13033             // different row.
13034             rows.push(crow);
13035             crow = {
13036                 start: cells[i],
13037                 end : cells[i]
13038             };
13039             
13040         }
13041         
13042         rows.push(crow);
13043         ev.els = [];
13044         ev.rows = rows;
13045         ev.cells = cells;
13046         
13047         cells[0].events.push(ev);
13048         
13049         this.calevents.push(ev);
13050     },
13051     
13052     clearEvents: function() {
13053         
13054         if(!this.calevents){
13055             return;
13056         }
13057         
13058         Roo.each(this.cells.elements, function(c){
13059             c.row = 0;
13060             c.events = [];
13061             c.more = [];
13062         });
13063         
13064         Roo.each(this.calevents, function(e) {
13065             Roo.each(e.els, function(el) {
13066                 el.un('mouseenter' ,this.onEventEnter, this);
13067                 el.un('mouseleave' ,this.onEventLeave, this);
13068                 el.remove();
13069             },this);
13070         },this);
13071         
13072         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
13073             e.remove();
13074         });
13075         
13076     },
13077     
13078     renderEvents: function()
13079     {   
13080         var _this = this;
13081         
13082         this.cells.each(function(c) {
13083             
13084             if(c.row < 5){
13085                 return;
13086             }
13087             
13088             var ev = c.events;
13089             
13090             var r = 4;
13091             if(c.row != c.events.length){
13092                 r = 4 - (4 - (c.row - c.events.length));
13093             }
13094             
13095             c.events = ev.slice(0, r);
13096             c.more = ev.slice(r);
13097             
13098             if(c.more.length && c.more.length == 1){
13099                 c.events.push(c.more.pop());
13100             }
13101             
13102             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
13103             
13104         });
13105             
13106         this.cells.each(function(c) {
13107             
13108             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
13109             
13110             
13111             for (var e = 0; e < c.events.length; e++){
13112                 var ev = c.events[e];
13113                 var rows = ev.rows;
13114                 
13115                 for(var i = 0; i < rows.length; i++) {
13116                 
13117                     // how many rows should it span..
13118
13119                     var  cfg = {
13120                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
13121                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
13122
13123                         unselectable : "on",
13124                         cn : [
13125                             {
13126                                 cls: 'fc-event-inner',
13127                                 cn : [
13128     //                                {
13129     //                                  tag:'span',
13130     //                                  cls: 'fc-event-time',
13131     //                                  html : cells.length > 1 ? '' : ev.time
13132     //                                },
13133                                     {
13134                                       tag:'span',
13135                                       cls: 'fc-event-title',
13136                                       html : String.format('{0}', ev.title)
13137                                     }
13138
13139
13140                                 ]
13141                             },
13142                             {
13143                                 cls: 'ui-resizable-handle ui-resizable-e',
13144                                 html : '&nbsp;&nbsp;&nbsp'
13145                             }
13146
13147                         ]
13148                     };
13149
13150                     if (i == 0) {
13151                         cfg.cls += ' fc-event-start';
13152                     }
13153                     if ((i+1) == rows.length) {
13154                         cfg.cls += ' fc-event-end';
13155                     }
13156
13157                     var ctr = _this.el.select('.fc-event-container',true).first();
13158                     var cg = ctr.createChild(cfg);
13159
13160                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
13161                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
13162
13163                     var r = (c.more.length) ? 1 : 0;
13164                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
13165                     cg.setWidth(ebox.right - sbox.x -2);
13166
13167                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
13168                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
13169                     cg.on('click', _this.onEventClick, _this, ev);
13170
13171                     ev.els.push(cg);
13172                     
13173                 }
13174                 
13175             }
13176             
13177             
13178             if(c.more.length){
13179                 var  cfg = {
13180                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
13181                     style : 'position: absolute',
13182                     unselectable : "on",
13183                     cn : [
13184                         {
13185                             cls: 'fc-event-inner',
13186                             cn : [
13187                                 {
13188                                   tag:'span',
13189                                   cls: 'fc-event-title',
13190                                   html : 'More'
13191                                 }
13192
13193
13194                             ]
13195                         },
13196                         {
13197                             cls: 'ui-resizable-handle ui-resizable-e',
13198                             html : '&nbsp;&nbsp;&nbsp'
13199                         }
13200
13201                     ]
13202                 };
13203
13204                 var ctr = _this.el.select('.fc-event-container',true).first();
13205                 var cg = ctr.createChild(cfg);
13206
13207                 var sbox = c.select('.fc-day-content',true).first().getBox();
13208                 var ebox = c.select('.fc-day-content',true).first().getBox();
13209                 //Roo.log(cg);
13210                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
13211                 cg.setWidth(ebox.right - sbox.x -2);
13212
13213                 cg.on('click', _this.onMoreEventClick, _this, c.more);
13214                 
13215             }
13216             
13217         });
13218         
13219         
13220         
13221     },
13222     
13223     onEventEnter: function (e, el,event,d) {
13224         this.fireEvent('evententer', this, el, event);
13225     },
13226     
13227     onEventLeave: function (e, el,event,d) {
13228         this.fireEvent('eventleave', this, el, event);
13229     },
13230     
13231     onEventClick: function (e, el,event,d) {
13232         this.fireEvent('eventclick', this, el, event);
13233     },
13234     
13235     onMonthChange: function () {
13236         this.store.load();
13237     },
13238     
13239     onMoreEventClick: function(e, el, more)
13240     {
13241         var _this = this;
13242         
13243         this.calpopover.placement = 'right';
13244         this.calpopover.setTitle('More');
13245         
13246         this.calpopover.setContent('');
13247         
13248         var ctr = this.calpopover.el.select('.popover-content', true).first();
13249         
13250         Roo.each(more, function(m){
13251             var cfg = {
13252                 cls : 'fc-event-hori fc-event-draggable',
13253                 html : m.title
13254             }
13255             var cg = ctr.createChild(cfg);
13256             
13257             cg.on('click', _this.onEventClick, _this, m);
13258         });
13259         
13260         this.calpopover.show(el);
13261         
13262         
13263     },
13264     
13265     onLoad: function () 
13266     {   
13267         this.calevents = [];
13268         var cal = this;
13269         
13270         if(this.store.getCount() > 0){
13271             this.store.data.each(function(d){
13272                cal.addItem({
13273                     id : d.data.id,
13274                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
13275                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
13276                     time : d.data.start_time,
13277                     title : d.data.title,
13278                     description : d.data.description,
13279                     venue : d.data.venue
13280                 });
13281             });
13282         }
13283         
13284         this.renderEvents();
13285         
13286         if(this.calevents.length && this.loadMask){
13287             this.maskEl.hide();
13288         }
13289     },
13290     
13291     onBeforeLoad: function()
13292     {
13293         this.clearEvents();
13294         if(this.loadMask){
13295             this.maskEl.show();
13296         }
13297     }
13298 });
13299
13300  
13301  /*
13302  * - LGPL
13303  *
13304  * element
13305  * 
13306  */
13307
13308 /**
13309  * @class Roo.bootstrap.Popover
13310  * @extends Roo.bootstrap.Component
13311  * Bootstrap Popover class
13312  * @cfg {String} html contents of the popover   (or false to use children..)
13313  * @cfg {String} title of popover (or false to hide)
13314  * @cfg {String} placement how it is placed
13315  * @cfg {String} trigger click || hover (or false to trigger manually)
13316  * @cfg {String} over what (parent or false to trigger manually.)
13317  * 
13318  * @constructor
13319  * Create a new Popover
13320  * @param {Object} config The config object
13321  */
13322
13323 Roo.bootstrap.Popover = function(config){
13324     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
13325 };
13326
13327 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
13328     
13329     title: 'Fill in a title',
13330     html: false,
13331     
13332     placement : 'right',
13333     trigger : 'hover', // hover
13334     
13335     over: 'parent',
13336     
13337     can_build_overlaid : false,
13338     
13339     getChildContainer : function()
13340     {
13341         return this.el.select('.popover-content',true).first();
13342     },
13343     
13344     getAutoCreate : function(){
13345          Roo.log('make popover?');
13346         var cfg = {
13347            cls : 'popover roo-dynamic',
13348            style: 'display:block',
13349            cn : [
13350                 {
13351                     cls : 'arrow'
13352                 },
13353                 {
13354                     cls : 'popover-inner',
13355                     cn : [
13356                         {
13357                             tag: 'h3',
13358                             cls: 'popover-title',
13359                             html : this.title
13360                         },
13361                         {
13362                             cls : 'popover-content',
13363                             html : this.html
13364                         }
13365                     ]
13366                     
13367                 }
13368            ]
13369         };
13370         
13371         return cfg;
13372     },
13373     setTitle: function(str)
13374     {
13375         this.el.select('.popover-title',true).first().dom.innerHTML = str;
13376     },
13377     setContent: function(str)
13378     {
13379         this.el.select('.popover-content',true).first().dom.innerHTML = str;
13380     },
13381     // as it get's added to the bottom of the page.
13382     onRender : function(ct, position)
13383     {
13384         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
13385         if(!this.el){
13386             var cfg = Roo.apply({},  this.getAutoCreate());
13387             cfg.id = Roo.id();
13388             
13389             if (this.cls) {
13390                 cfg.cls += ' ' + this.cls;
13391             }
13392             if (this.style) {
13393                 cfg.style = this.style;
13394             }
13395             Roo.log("adding to ")
13396             this.el = Roo.get(document.body).createChild(cfg, position);
13397             Roo.log(this.el);
13398         }
13399         this.initEvents();
13400     },
13401     
13402     initEvents : function()
13403     {
13404         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
13405         this.el.enableDisplayMode('block');
13406         this.el.hide();
13407         if (this.over === false) {
13408             return; 
13409         }
13410         if (this.triggers === false) {
13411             return;
13412         }
13413         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13414         var triggers = this.trigger ? this.trigger.split(' ') : [];
13415         Roo.each(triggers, function(trigger) {
13416         
13417             if (trigger == 'click') {
13418                 on_el.on('click', this.toggle, this);
13419             } else if (trigger != 'manual') {
13420                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
13421                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
13422       
13423                 on_el.on(eventIn  ,this.enter, this);
13424                 on_el.on(eventOut, this.leave, this);
13425             }
13426         }, this);
13427         
13428     },
13429     
13430     
13431     // private
13432     timeout : null,
13433     hoverState : null,
13434     
13435     toggle : function () {
13436         this.hoverState == 'in' ? this.leave() : this.enter();
13437     },
13438     
13439     enter : function () {
13440        
13441     
13442         clearTimeout(this.timeout);
13443     
13444         this.hoverState = 'in'
13445     
13446         if (!this.delay || !this.delay.show) {
13447             this.show();
13448             return 
13449         }
13450         var _t = this;
13451         this.timeout = setTimeout(function () {
13452             if (_t.hoverState == 'in') {
13453                 _t.show();
13454             }
13455         }, this.delay.show)
13456     },
13457     leave : function() {
13458         clearTimeout(this.timeout);
13459     
13460         this.hoverState = 'out'
13461     
13462         if (!this.delay || !this.delay.hide) {
13463             this.hide();
13464             return 
13465         }
13466         var _t = this;
13467         this.timeout = setTimeout(function () {
13468             if (_t.hoverState == 'out') {
13469                 _t.hide();
13470             }
13471         }, this.delay.hide)
13472     },
13473     
13474     show : function (on_el)
13475     {
13476         if (!on_el) {
13477             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13478         }
13479         // set content.
13480         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
13481         if (this.html !== false) {
13482             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
13483         }
13484         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
13485         if (!this.title.length) {
13486             this.el.select('.popover-title',true).hide();
13487         }
13488         
13489         var placement = typeof this.placement == 'function' ?
13490             this.placement.call(this, this.el, on_el) :
13491             this.placement;
13492             
13493         var autoToken = /\s?auto?\s?/i;
13494         var autoPlace = autoToken.test(placement);
13495         if (autoPlace) {
13496             placement = placement.replace(autoToken, '') || 'top';
13497         }
13498         
13499         //this.el.detach()
13500         //this.el.setXY([0,0]);
13501         this.el.show();
13502         this.el.dom.style.display='block';
13503         this.el.addClass(placement);
13504         
13505         //this.el.appendTo(on_el);
13506         
13507         var p = this.getPosition();
13508         var box = this.el.getBox();
13509         
13510         if (autoPlace) {
13511             // fixme..
13512         }
13513         var align = Roo.bootstrap.Popover.alignment[placement]
13514         this.el.alignTo(on_el, align[0],align[1]);
13515         //var arrow = this.el.select('.arrow',true).first();
13516         //arrow.set(align[2], 
13517         
13518         this.el.addClass('in');
13519         this.hoverState = null;
13520         
13521         if (this.el.hasClass('fade')) {
13522             // fade it?
13523         }
13524         
13525     },
13526     hide : function()
13527     {
13528         this.el.setXY([0,0]);
13529         this.el.removeClass('in');
13530         this.el.hide();
13531         
13532     }
13533     
13534 });
13535
13536 Roo.bootstrap.Popover.alignment = {
13537     'left' : ['r-l', [-10,0], 'right'],
13538     'right' : ['l-r', [10,0], 'left'],
13539     'bottom' : ['t-b', [0,10], 'top'],
13540     'top' : [ 'b-t', [0,-10], 'bottom']
13541 };
13542
13543  /*
13544  * - LGPL
13545  *
13546  * Progress
13547  * 
13548  */
13549
13550 /**
13551  * @class Roo.bootstrap.Progress
13552  * @extends Roo.bootstrap.Component
13553  * Bootstrap Progress class
13554  * @cfg {Boolean} striped striped of the progress bar
13555  * @cfg {Boolean} active animated of the progress bar
13556  * 
13557  * 
13558  * @constructor
13559  * Create a new Progress
13560  * @param {Object} config The config object
13561  */
13562
13563 Roo.bootstrap.Progress = function(config){
13564     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
13565 };
13566
13567 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
13568     
13569     striped : false,
13570     active: false,
13571     
13572     getAutoCreate : function(){
13573         var cfg = {
13574             tag: 'div',
13575             cls: 'progress'
13576         };
13577         
13578         
13579         if(this.striped){
13580             cfg.cls += ' progress-striped';
13581         }
13582       
13583         if(this.active){
13584             cfg.cls += ' active';
13585         }
13586         
13587         
13588         return cfg;
13589     }
13590    
13591 });
13592
13593  
13594
13595  /*
13596  * - LGPL
13597  *
13598  * ProgressBar
13599  * 
13600  */
13601
13602 /**
13603  * @class Roo.bootstrap.ProgressBar
13604  * @extends Roo.bootstrap.Component
13605  * Bootstrap ProgressBar class
13606  * @cfg {Number} aria_valuenow aria-value now
13607  * @cfg {Number} aria_valuemin aria-value min
13608  * @cfg {Number} aria_valuemax aria-value max
13609  * @cfg {String} label label for the progress bar
13610  * @cfg {String} panel (success | info | warning | danger )
13611  * @cfg {String} role role of the progress bar
13612  * @cfg {String} sr_only text
13613  * 
13614  * 
13615  * @constructor
13616  * Create a new ProgressBar
13617  * @param {Object} config The config object
13618  */
13619
13620 Roo.bootstrap.ProgressBar = function(config){
13621     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
13622 };
13623
13624 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
13625     
13626     aria_valuenow : 0,
13627     aria_valuemin : 0,
13628     aria_valuemax : 100,
13629     label : false,
13630     panel : false,
13631     role : false,
13632     sr_only: false,
13633     
13634     getAutoCreate : function()
13635     {
13636         
13637         var cfg = {
13638             tag: 'div',
13639             cls: 'progress-bar',
13640             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
13641         };
13642         
13643         if(this.sr_only){
13644             cfg.cn = {
13645                 tag: 'span',
13646                 cls: 'sr-only',
13647                 html: this.sr_only
13648             }
13649         }
13650         
13651         if(this.role){
13652             cfg.role = this.role;
13653         }
13654         
13655         if(this.aria_valuenow){
13656             cfg['aria-valuenow'] = this.aria_valuenow;
13657         }
13658         
13659         if(this.aria_valuemin){
13660             cfg['aria-valuemin'] = this.aria_valuemin;
13661         }
13662         
13663         if(this.aria_valuemax){
13664             cfg['aria-valuemax'] = this.aria_valuemax;
13665         }
13666         
13667         if(this.label && !this.sr_only){
13668             cfg.html = this.label;
13669         }
13670         
13671         if(this.panel){
13672             cfg.cls += ' progress-bar-' + this.panel;
13673         }
13674         
13675         return cfg;
13676     },
13677     
13678     update : function(aria_valuenow)
13679     {
13680         this.aria_valuenow = aria_valuenow;
13681         
13682         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
13683     }
13684    
13685 });
13686
13687  
13688
13689  /*
13690  * - LGPL
13691  *
13692  * column
13693  * 
13694  */
13695
13696 /**
13697  * @class Roo.bootstrap.TabGroup
13698  * @extends Roo.bootstrap.Column
13699  * Bootstrap Column class
13700  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
13701  * 
13702  * @constructor
13703  * Create a new TabGroup
13704  * @param {Object} config The config object
13705  */
13706
13707 Roo.bootstrap.TabGroup = function(config){
13708     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
13709     if (!this.navId) {
13710         this.navId = Roo.id();
13711     }
13712     this.tabs = [];
13713     Roo.bootstrap.TabGroup.register(this);
13714     
13715 };
13716
13717 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
13718     
13719      
13720     getAutoCreate : function(){
13721         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
13722         
13723         cfg.cls += ' tab-content';
13724         
13725         return cfg;
13726     },
13727     
13728     /**
13729     * register a Navigation item
13730     * @param {Roo.bootstrap.NavItem} the navitem to add
13731     */
13732     register : function(item)
13733     {
13734         this.tabs.push( item);
13735         item.navId = this.navId; // not really needed..
13736     
13737     },
13738     
13739     getActivePanel : function()
13740     {
13741         var r = false;
13742         Roo.each(this.tabs, function(t) {
13743             if (t.active) {
13744                 r = t;
13745                 return false;
13746             }
13747             return null;
13748         });
13749         return r;
13750         
13751     },
13752     getPanelByName : function(n)
13753     {
13754         var r = false;
13755         Roo.each(this.tabs, function(t) {
13756             if (t.tabId == n) {
13757                 r = t;
13758                 return false;
13759             }
13760             return null;
13761         });
13762         return r;
13763     },
13764     indexOfPanel : function(p)
13765     {
13766         var r = false;
13767         Roo.each(this.tabs, function(t,i) {
13768             if (t.tabId == p.tabId) {
13769                 r = i;
13770                 return false;
13771             }
13772             return null;
13773         });
13774         return r;
13775     },
13776     showPanel : function (pan)
13777     {
13778         if (typeof(pan) == 'pnumber') {
13779             pan = this.tabs[pan];
13780         }
13781         if (typeof(pan) == 'string') {
13782             pan = this.getPanelByName(pan);
13783         }
13784         if (pan.tabId == this.getActivePanel().tabId) {
13785             return;
13786         }
13787         this.getActivePanel().setActive(false);
13788         pan.setActive(true);
13789         
13790     },
13791     showNextPanel : function()
13792     {
13793         var i = this.indexOfPanel(this.getActivePanel());
13794         if (i > this.tabs.length) {
13795             return;
13796         }
13797         this.showPanel(this.tabs[i+1]);
13798     },
13799     showPrevPanel : function()
13800     {
13801         var i = this.indexOfPanel(this.getActivePanel());
13802         if (i  < 1) {
13803             return;
13804         }
13805         this.showPanel(this.tabs[i-1]);
13806     }
13807     
13808     
13809   
13810 });
13811
13812  
13813
13814  
13815  
13816 Roo.apply(Roo.bootstrap.TabGroup, {
13817     
13818     groups: {},
13819      /**
13820     * register a Navigation Group
13821     * @param {Roo.bootstrap.NavGroup} the navgroup to add
13822     */
13823     register : function(navgrp)
13824     {
13825         this.groups[navgrp.navId] = navgrp;
13826         
13827     },
13828     /**
13829     * fetch a Navigation Group based on the navigation ID
13830     * if one does not exist , it will get created.
13831     * @param {string} the navgroup to add
13832     * @returns {Roo.bootstrap.NavGroup} the navgroup 
13833     */
13834     get: function(navId) {
13835         if (typeof(this.groups[navId]) == 'undefined') {
13836             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
13837         }
13838         return this.groups[navId] ;
13839     }
13840     
13841     
13842     
13843 });
13844
13845  /*
13846  * - LGPL
13847  *
13848  * TabPanel
13849  * 
13850  */
13851
13852 /**
13853  * @class Roo.bootstrap.TabPanel
13854  * @extends Roo.bootstrap.Component
13855  * Bootstrap TabPanel class
13856  * @cfg {Boolean} active panel active
13857  * @cfg {String} html panel content
13858  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
13859  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
13860  * 
13861  * 
13862  * @constructor
13863  * Create a new TabPanel
13864  * @param {Object} config The config object
13865  */
13866
13867 Roo.bootstrap.TabPanel = function(config){
13868     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
13869      this.addEvents({
13870         /**
13871              * @event changed
13872              * Fires when the active status changes
13873              * @param {Roo.bootstrap.TabPanel} this
13874              * @param {Boolean} state the new state
13875             
13876          */
13877         'changed': true
13878      });
13879     this.tabId = this.tabId || Roo.id();
13880   
13881 };
13882
13883 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
13884     
13885     active: false,
13886     html: false,
13887     tabId: false,
13888     navId : false,
13889     
13890     getAutoCreate : function(){
13891         var cfg = {
13892             tag: 'div',
13893             cls: 'tab-pane',
13894             html: this.html || ''
13895         };
13896         
13897         if(this.active){
13898             cfg.cls += ' active';
13899         }
13900         
13901         if(this.tabId){
13902             cfg.tabId = this.tabId;
13903         }
13904         
13905         return cfg;
13906     },
13907     
13908     initEvents:  function()
13909     {
13910         Roo.log('-------- init events on tab panel ---------');
13911         
13912         var p = this.parent();
13913         this.navId = this.navId || p.navId;
13914         
13915         if (typeof(this.navId) != 'undefined') {
13916             // not really needed.. but just in case.. parent should be a NavGroup.
13917             var tg = Roo.bootstrap.TabGroup.get(this.navId);
13918              Roo.log(['register', tg, this]);
13919             tg.register(this);
13920         }
13921     },
13922     
13923     
13924     onRender : function(ct, position)
13925     {
13926        // Roo.log("Call onRender: " + this.xtype);
13927         
13928         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
13929         
13930         // registration with navgroups..
13931         if (this.navId && this.tabId) {
13932             var grp = Roo.bootstrap.NavGroup.get(this.navId);
13933             if (grp) {
13934                 //code
13935                 var item = grp.getNavItem(this.tabId);
13936                 if (!item) {
13937                     Roo.log("could not find navID:"  + this.navId + ", tabId: " + this.tabId);
13938                 } else {
13939                     item.on('changed', function(item, state) {
13940                         this.setActive(state);
13941                     }, this);
13942                 }
13943             }
13944         }
13945         
13946         
13947         
13948     },
13949     setActive: function(state)
13950     {
13951         Roo.log("panel - set active " + this.tabId + "=" + state);
13952         
13953         this.active = state;
13954         if (!state) {
13955             this.el.removeClass('active');
13956             
13957         } else  if (!this.el.hasClass('active')) {
13958             this.el.addClass('active');
13959         }
13960         this.fireEvent('changed', this, state);
13961     }
13962     
13963     
13964 });
13965  
13966
13967  
13968
13969  /*
13970  * - LGPL
13971  *
13972  * DateField
13973  * 
13974  */
13975
13976 /**
13977  * @class Roo.bootstrap.DateField
13978  * @extends Roo.bootstrap.Input
13979  * Bootstrap DateField class
13980  * @cfg {Number} weekStart default 0
13981  * @cfg {Number} weekStart default 0
13982  * @cfg {Number} viewMode default empty, (months|years)
13983  * @cfg {Number} minViewMode default empty, (months|years)
13984  * @cfg {Number} startDate default -Infinity
13985  * @cfg {Number} endDate default Infinity
13986  * @cfg {Boolean} todayHighlight default false
13987  * @cfg {Boolean} todayBtn default false
13988  * @cfg {Boolean} calendarWeeks default false
13989  * @cfg {Object} daysOfWeekDisabled default empty
13990  * 
13991  * @cfg {Boolean} keyboardNavigation default true
13992  * @cfg {String} language default en
13993  * 
13994  * @constructor
13995  * Create a new DateField
13996  * @param {Object} config The config object
13997  */
13998
13999 Roo.bootstrap.DateField = function(config){
14000     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
14001      this.addEvents({
14002             /**
14003              * @event show
14004              * Fires when this field show.
14005              * @param {Roo.bootstrap.DateField} this
14006              * @param {Mixed} date The date value
14007              */
14008             show : true,
14009             /**
14010              * @event show
14011              * Fires when this field hide.
14012              * @param {Roo.bootstrap.DateField} this
14013              * @param {Mixed} date The date value
14014              */
14015             hide : true,
14016             /**
14017              * @event select
14018              * Fires when select a date.
14019              * @param {Roo.bootstrap.DateField} this
14020              * @param {Mixed} date The date value
14021              */
14022             select : true
14023         });
14024 };
14025
14026 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
14027     
14028     /**
14029      * @cfg {String} format
14030      * The default date format string which can be overriden for localization support.  The format must be
14031      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
14032      */
14033     format : "m/d/y",
14034     /**
14035      * @cfg {String} altFormats
14036      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
14037      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
14038      */
14039     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
14040     
14041     weekStart : 0,
14042     
14043     viewMode : '',
14044     
14045     minViewMode : '',
14046     
14047     todayHighlight : false,
14048     
14049     todayBtn: false,
14050     
14051     language: 'en',
14052     
14053     keyboardNavigation: true,
14054     
14055     calendarWeeks: false,
14056     
14057     startDate: -Infinity,
14058     
14059     endDate: Infinity,
14060     
14061     daysOfWeekDisabled: [],
14062     
14063     _events: [],
14064     
14065     UTCDate: function()
14066     {
14067         return new Date(Date.UTC.apply(Date, arguments));
14068     },
14069     
14070     UTCToday: function()
14071     {
14072         var today = new Date();
14073         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
14074     },
14075     
14076     getDate: function() {
14077             var d = this.getUTCDate();
14078             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
14079     },
14080     
14081     getUTCDate: function() {
14082             return this.date;
14083     },
14084     
14085     setDate: function(d) {
14086             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
14087     },
14088     
14089     setUTCDate: function(d) {
14090             this.date = d;
14091             this.setValue(this.formatDate(this.date));
14092     },
14093         
14094     onRender: function(ct, position)
14095     {
14096         
14097         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
14098         
14099         this.language = this.language || 'en';
14100         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
14101         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
14102         
14103         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
14104         this.format = this.format || 'm/d/y';
14105         this.isInline = false;
14106         this.isInput = true;
14107         this.component = this.el.select('.add-on', true).first() || false;
14108         this.component = (this.component && this.component.length === 0) ? false : this.component;
14109         this.hasInput = this.component && this.inputEL().length;
14110         
14111         if (typeof(this.minViewMode === 'string')) {
14112             switch (this.minViewMode) {
14113                 case 'months':
14114                     this.minViewMode = 1;
14115                     break;
14116                 case 'years':
14117                     this.minViewMode = 2;
14118                     break;
14119                 default:
14120                     this.minViewMode = 0;
14121                     break;
14122             }
14123         }
14124         
14125         if (typeof(this.viewMode === 'string')) {
14126             switch (this.viewMode) {
14127                 case 'months':
14128                     this.viewMode = 1;
14129                     break;
14130                 case 'years':
14131                     this.viewMode = 2;
14132                     break;
14133                 default:
14134                     this.viewMode = 0;
14135                     break;
14136             }
14137         }
14138                 
14139         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
14140         
14141         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14142         
14143         this.picker().on('mousedown', this.onMousedown, this);
14144         this.picker().on('click', this.onClick, this);
14145         
14146         this.picker().addClass('datepicker-dropdown');
14147         
14148         this.startViewMode = this.viewMode;
14149         
14150         
14151         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
14152             if(!this.calendarWeeks){
14153                 v.remove();
14154                 return;
14155             };
14156             
14157             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
14158             v.attr('colspan', function(i, val){
14159                 return parseInt(val) + 1;
14160             });
14161         })
14162                         
14163         
14164         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
14165         
14166         this.setStartDate(this.startDate);
14167         this.setEndDate(this.endDate);
14168         
14169         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
14170         
14171         this.fillDow();
14172         this.fillMonths();
14173         this.update();
14174         this.showMode();
14175         
14176         if(this.isInline) {
14177             this.show();
14178         }
14179     },
14180     
14181     picker : function()
14182     {
14183         return this.el.select('.datepicker', true).first();
14184     },
14185     
14186     fillDow: function()
14187     {
14188         var dowCnt = this.weekStart;
14189         
14190         var dow = {
14191             tag: 'tr',
14192             cn: [
14193                 
14194             ]
14195         };
14196         
14197         if(this.calendarWeeks){
14198             dow.cn.push({
14199                 tag: 'th',
14200                 cls: 'cw',
14201                 html: '&nbsp;'
14202             })
14203         }
14204         
14205         while (dowCnt < this.weekStart + 7) {
14206             dow.cn.push({
14207                 tag: 'th',
14208                 cls: 'dow',
14209                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
14210             });
14211         }
14212         
14213         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
14214     },
14215     
14216     fillMonths: function()
14217     {    
14218         var i = 0
14219         var months = this.picker().select('>.datepicker-months td', true).first();
14220         
14221         months.dom.innerHTML = '';
14222         
14223         while (i < 12) {
14224             var month = {
14225                 tag: 'span',
14226                 cls: 'month',
14227                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
14228             }
14229             
14230             months.createChild(month);
14231         }
14232         
14233     },
14234     
14235     update: function()
14236     {
14237         
14238         this.date = (typeof(this.date) === 'undefined') ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
14239         
14240         if (this.date < this.startDate) {
14241             this.viewDate = new Date(this.startDate);
14242         } else if (this.date > this.endDate) {
14243             this.viewDate = new Date(this.endDate);
14244         } else {
14245             this.viewDate = new Date(this.date);
14246         }
14247         
14248         this.fill();
14249     },
14250     
14251     fill: function() 
14252     {
14253         var d = new Date(this.viewDate),
14254                 year = d.getUTCFullYear(),
14255                 month = d.getUTCMonth(),
14256                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
14257                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
14258                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
14259                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
14260                 currentDate = this.date && this.date.valueOf(),
14261                 today = this.UTCToday();
14262         
14263         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
14264         
14265 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
14266         
14267 //        this.picker.select('>tfoot th.today').
14268 //                                              .text(dates[this.language].today)
14269 //                                              .toggle(this.todayBtn !== false);
14270     
14271         this.updateNavArrows();
14272         this.fillMonths();
14273                                                 
14274         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
14275         
14276         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
14277          
14278         prevMonth.setUTCDate(day);
14279         
14280         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
14281         
14282         var nextMonth = new Date(prevMonth);
14283         
14284         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
14285         
14286         nextMonth = nextMonth.valueOf();
14287         
14288         var fillMonths = false;
14289         
14290         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
14291         
14292         while(prevMonth.valueOf() < nextMonth) {
14293             var clsName = '';
14294             
14295             if (prevMonth.getUTCDay() === this.weekStart) {
14296                 if(fillMonths){
14297                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
14298                 }
14299                     
14300                 fillMonths = {
14301                     tag: 'tr',
14302                     cn: []
14303                 };
14304                 
14305                 if(this.calendarWeeks){
14306                     // ISO 8601: First week contains first thursday.
14307                     // ISO also states week starts on Monday, but we can be more abstract here.
14308                     var
14309                     // Start of current week: based on weekstart/current date
14310                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
14311                     // Thursday of this week
14312                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
14313                     // First Thursday of year, year from thursday
14314                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
14315                     // Calendar week: ms between thursdays, div ms per day, div 7 days
14316                     calWeek =  (th - yth) / 864e5 / 7 + 1;
14317                     
14318                     fillMonths.cn.push({
14319                         tag: 'td',
14320                         cls: 'cw',
14321                         html: calWeek
14322                     });
14323                 }
14324             }
14325             
14326             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
14327                 clsName += ' old';
14328             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
14329                 clsName += ' new';
14330             }
14331             if (this.todayHighlight &&
14332                 prevMonth.getUTCFullYear() == today.getFullYear() &&
14333                 prevMonth.getUTCMonth() == today.getMonth() &&
14334                 prevMonth.getUTCDate() == today.getDate()) {
14335                 clsName += ' today';
14336             }
14337             
14338             if (currentDate && prevMonth.valueOf() === currentDate) {
14339                 clsName += ' active';
14340             }
14341             
14342             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
14343                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
14344                     clsName += ' disabled';
14345             }
14346             
14347             fillMonths.cn.push({
14348                 tag: 'td',
14349                 cls: 'day ' + clsName,
14350                 html: prevMonth.getDate()
14351             })
14352             
14353             prevMonth.setDate(prevMonth.getDate()+1);
14354         }
14355           
14356         var currentYear = this.date && this.date.getUTCFullYear();
14357         var currentMonth = this.date && this.date.getUTCMonth();
14358         
14359         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
14360         
14361         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
14362             v.removeClass('active');
14363             
14364             if(currentYear === year && k === currentMonth){
14365                 v.addClass('active');
14366             }
14367             
14368             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
14369                 v.addClass('disabled');
14370             }
14371             
14372         });
14373         
14374         
14375         year = parseInt(year/10, 10) * 10;
14376         
14377         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
14378         
14379         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
14380         
14381         year -= 1;
14382         for (var i = -1; i < 11; i++) {
14383             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
14384                 tag: 'span',
14385                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
14386                 html: year
14387             })
14388             
14389             year += 1;
14390         }
14391     },
14392     
14393     showMode: function(dir) 
14394     {
14395         if (dir) {
14396             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
14397         }
14398         Roo.each(this.picker().select('>div',true).elements, function(v){
14399             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14400             v.hide();
14401         });
14402         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
14403     },
14404     
14405     place: function()
14406     {
14407         if(this.isInline) return;
14408         
14409         this.picker().removeClass(['bottom', 'top']);
14410         
14411         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
14412             /*
14413              * place to the top of element!
14414              *
14415              */
14416             
14417             this.picker().addClass('top');
14418             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
14419             
14420             return;
14421         }
14422         
14423         this.picker().addClass('bottom');
14424         
14425         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
14426     },
14427     
14428     parseDate : function(value)
14429     {
14430         if(!value || value instanceof Date){
14431             return value;
14432         }
14433         var v = Date.parseDate(value, this.format);
14434         if (!v && this.useIso) {
14435             v = Date.parseDate(value, 'Y-m-d');
14436         }
14437         if(!v && this.altFormats){
14438             if(!this.altFormatsArray){
14439                 this.altFormatsArray = this.altFormats.split("|");
14440             }
14441             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
14442                 v = Date.parseDate(value, this.altFormatsArray[i]);
14443             }
14444         }
14445         return v;
14446     },
14447     
14448     formatDate : function(date, fmt)
14449     {
14450         return (!date || !(date instanceof Date)) ?
14451         date : date.dateFormat(fmt || this.format);
14452     },
14453     
14454     onFocus : function()
14455     {
14456         Roo.bootstrap.DateField.superclass.onFocus.call(this);
14457         this.show();
14458     },
14459     
14460     onBlur : function()
14461     {
14462         Roo.bootstrap.DateField.superclass.onBlur.call(this);
14463         
14464         var d = this.inputEl().getValue();
14465         
14466         if(d && d.length){
14467             this.setValue(d);
14468         }
14469                 
14470         this.hide();
14471     },
14472     
14473     show : function()
14474     {
14475         this.picker().show();
14476         this.update();
14477         this.place();
14478         
14479         this.fireEvent('show', this, this.date);
14480     },
14481     
14482     hide : function()
14483     {
14484         if(this.isInline) return;
14485         this.picker().hide();
14486         this.viewMode = this.startViewMode;
14487         this.showMode();
14488         
14489         this.fireEvent('hide', this, this.date);
14490         
14491     },
14492     
14493     onMousedown: function(e)
14494     {
14495         e.stopPropagation();
14496         e.preventDefault();
14497     },
14498     
14499     keyup: function(e)
14500     {
14501         Roo.bootstrap.DateField.superclass.keyup.call(this);
14502         this.update();
14503     },
14504
14505     setValue: function(v)
14506     {
14507         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
14508         
14509         var d = new Date(v);
14510         
14511         if(isNaN(d.getTime())){
14512             return;
14513         }
14514         
14515         this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
14516
14517         this.update();
14518
14519         this.fireEvent('select', this, this.date);
14520         
14521     },
14522     
14523     getValue: function()
14524     {
14525         return this.formatDate(this.date);
14526     },
14527     
14528     fireKey: function(e)
14529     {
14530         if (!this.picker().isVisible()){
14531             if (e.keyCode == 27) // allow escape to hide and re-show picker
14532                 this.show();
14533             return;
14534         }
14535         var dateChanged = false,
14536         dir, day, month,
14537         newDate, newViewDate;
14538         
14539         switch(e.keyCode){
14540             case 27: // escape
14541                 this.hide();
14542                 e.preventDefault();
14543                 break;
14544             case 37: // left
14545             case 39: // right
14546                 if (!this.keyboardNavigation) break;
14547                 dir = e.keyCode == 37 ? -1 : 1;
14548                 
14549                 if (e.ctrlKey){
14550                     newDate = this.moveYear(this.date, dir);
14551                     newViewDate = this.moveYear(this.viewDate, dir);
14552                 } else if (e.shiftKey){
14553                     newDate = this.moveMonth(this.date, dir);
14554                     newViewDate = this.moveMonth(this.viewDate, dir);
14555                 } else {
14556                     newDate = new Date(this.date);
14557                     newDate.setUTCDate(this.date.getUTCDate() + dir);
14558                     newViewDate = new Date(this.viewDate);
14559                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
14560                 }
14561                 if (this.dateWithinRange(newDate)){
14562                     this.date = newDate;
14563                     this.viewDate = newViewDate;
14564                     this.setValue(this.formatDate(this.date));
14565 //                    this.update();
14566                     e.preventDefault();
14567                     dateChanged = true;
14568                 }
14569                 break;
14570             case 38: // up
14571             case 40: // down
14572                 if (!this.keyboardNavigation) break;
14573                 dir = e.keyCode == 38 ? -1 : 1;
14574                 if (e.ctrlKey){
14575                     newDate = this.moveYear(this.date, dir);
14576                     newViewDate = this.moveYear(this.viewDate, dir);
14577                 } else if (e.shiftKey){
14578                     newDate = this.moveMonth(this.date, dir);
14579                     newViewDate = this.moveMonth(this.viewDate, dir);
14580                 } else {
14581                     newDate = new Date(this.date);
14582                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
14583                     newViewDate = new Date(this.viewDate);
14584                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
14585                 }
14586                 if (this.dateWithinRange(newDate)){
14587                     this.date = newDate;
14588                     this.viewDate = newViewDate;
14589                     this.setValue(this.formatDate(this.date));
14590 //                    this.update();
14591                     e.preventDefault();
14592                     dateChanged = true;
14593                 }
14594                 break;
14595             case 13: // enter
14596                 this.setValue(this.formatDate(this.date));
14597                 this.hide();
14598                 e.preventDefault();
14599                 break;
14600             case 9: // tab
14601                 this.setValue(this.formatDate(this.date));
14602                 this.hide();
14603                 break;
14604                 
14605         }
14606     },
14607     
14608     
14609     onClick: function(e) 
14610     {
14611         e.stopPropagation();
14612         e.preventDefault();
14613         
14614         var target = e.getTarget();
14615         
14616         if(target.nodeName.toLowerCase() === 'i'){
14617             target = Roo.get(target).dom.parentNode;
14618         }
14619         
14620         var nodeName = target.nodeName;
14621         var className = target.className;
14622         var html = target.innerHTML;
14623         
14624         switch(nodeName.toLowerCase()) {
14625             case 'th':
14626                 switch(className) {
14627                     case 'switch':
14628                         this.showMode(1);
14629                         break;
14630                     case 'prev':
14631                     case 'next':
14632                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
14633                         switch(this.viewMode){
14634                                 case 0:
14635                                         this.viewDate = this.moveMonth(this.viewDate, dir);
14636                                         break;
14637                                 case 1:
14638                                 case 2:
14639                                         this.viewDate = this.moveYear(this.viewDate, dir);
14640                                         break;
14641                         }
14642                         this.fill();
14643                         break;
14644                     case 'today':
14645                         var date = new Date();
14646                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
14647 //                        this.fill()
14648                         this.setValue(this.formatDate(this.date));
14649                         
14650                         this.hide();
14651                         break;
14652                 }
14653                 break;
14654             case 'span':
14655                 if (className.indexOf('disabled') === -1) {
14656                     this.viewDate.setUTCDate(1);
14657                     if (className.indexOf('month') !== -1) {
14658                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
14659                     } else {
14660                         var year = parseInt(html, 10) || 0;
14661                         this.viewDate.setUTCFullYear(year);
14662                         
14663                     }
14664                     this.showMode(-1);
14665                     this.fill();
14666                 }
14667                 break;
14668                 
14669             case 'td':
14670                 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
14671                     var day = parseInt(html, 10) || 1;
14672                     var year = this.viewDate.getUTCFullYear(),
14673                         month = this.viewDate.getUTCMonth();
14674
14675                     if (className.indexOf('old') !== -1) {
14676                         if(month === 0 ){
14677                             month = 11;
14678                             year -= 1;
14679                         }else{
14680                             month -= 1;
14681                         }
14682                     } else if (className.indexOf('new') !== -1) {
14683                         if (month == 11) {
14684                             month = 0;
14685                             year += 1;
14686                         } else {
14687                             month += 1;
14688                         }
14689                     }
14690                     this.date = this.UTCDate(year, month, day,0,0,0,0);
14691                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
14692 //                    this.fill();
14693                     this.setValue(this.formatDate(this.date));
14694                     this.hide();
14695                 }
14696                 break;
14697         }
14698     },
14699     
14700     setStartDate: function(startDate)
14701     {
14702         this.startDate = startDate || -Infinity;
14703         if (this.startDate !== -Infinity) {
14704             this.startDate = this.parseDate(this.startDate);
14705         }
14706         this.update();
14707         this.updateNavArrows();
14708     },
14709
14710     setEndDate: function(endDate)
14711     {
14712         this.endDate = endDate || Infinity;
14713         if (this.endDate !== Infinity) {
14714             this.endDate = this.parseDate(this.endDate);
14715         }
14716         this.update();
14717         this.updateNavArrows();
14718     },
14719     
14720     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
14721     {
14722         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
14723         if (typeof(this.daysOfWeekDisabled) !== 'object') {
14724             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
14725         }
14726         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
14727             return parseInt(d, 10);
14728         });
14729         this.update();
14730         this.updateNavArrows();
14731     },
14732     
14733     updateNavArrows: function() 
14734     {
14735         var d = new Date(this.viewDate),
14736         year = d.getUTCFullYear(),
14737         month = d.getUTCMonth();
14738         
14739         Roo.each(this.picker().select('.prev', true).elements, function(v){
14740             v.show();
14741             switch (this.viewMode) {
14742                 case 0:
14743
14744                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
14745                         v.hide();
14746                     }
14747                     break;
14748                 case 1:
14749                 case 2:
14750                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
14751                         v.hide();
14752                     }
14753                     break;
14754             }
14755         });
14756         
14757         Roo.each(this.picker().select('.next', true).elements, function(v){
14758             v.show();
14759             switch (this.viewMode) {
14760                 case 0:
14761
14762                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
14763                         v.hide();
14764                     }
14765                     break;
14766                 case 1:
14767                 case 2:
14768                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
14769                         v.hide();
14770                     }
14771                     break;
14772             }
14773         })
14774     },
14775     
14776     moveMonth: function(date, dir)
14777     {
14778         if (!dir) return date;
14779         var new_date = new Date(date.valueOf()),
14780         day = new_date.getUTCDate(),
14781         month = new_date.getUTCMonth(),
14782         mag = Math.abs(dir),
14783         new_month, test;
14784         dir = dir > 0 ? 1 : -1;
14785         if (mag == 1){
14786             test = dir == -1
14787             // If going back one month, make sure month is not current month
14788             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
14789             ? function(){
14790                 return new_date.getUTCMonth() == month;
14791             }
14792             // If going forward one month, make sure month is as expected
14793             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
14794             : function(){
14795                 return new_date.getUTCMonth() != new_month;
14796             };
14797             new_month = month + dir;
14798             new_date.setUTCMonth(new_month);
14799             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
14800             if (new_month < 0 || new_month > 11)
14801                 new_month = (new_month + 12) % 12;
14802         } else {
14803             // For magnitudes >1, move one month at a time...
14804             for (var i=0; i<mag; i++)
14805                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
14806                 new_date = this.moveMonth(new_date, dir);
14807             // ...then reset the day, keeping it in the new month
14808             new_month = new_date.getUTCMonth();
14809             new_date.setUTCDate(day);
14810             test = function(){
14811                 return new_month != new_date.getUTCMonth();
14812             };
14813         }
14814         // Common date-resetting loop -- if date is beyond end of month, make it
14815         // end of month
14816         while (test()){
14817             new_date.setUTCDate(--day);
14818             new_date.setUTCMonth(new_month);
14819         }
14820         return new_date;
14821     },
14822
14823     moveYear: function(date, dir)
14824     {
14825         return this.moveMonth(date, dir*12);
14826     },
14827
14828     dateWithinRange: function(date)
14829     {
14830         return date >= this.startDate && date <= this.endDate;
14831     },
14832
14833     
14834     remove: function() 
14835     {
14836         this.picker().remove();
14837     }
14838    
14839 });
14840
14841 Roo.apply(Roo.bootstrap.DateField,  {
14842     
14843     head : {
14844         tag: 'thead',
14845         cn: [
14846         {
14847             tag: 'tr',
14848             cn: [
14849             {
14850                 tag: 'th',
14851                 cls: 'prev',
14852                 html: '<i class="fa fa-arrow-left"/>'
14853             },
14854             {
14855                 tag: 'th',
14856                 cls: 'switch',
14857                 colspan: '5'
14858             },
14859             {
14860                 tag: 'th',
14861                 cls: 'next',
14862                 html: '<i class="fa fa-arrow-right"/>'
14863             }
14864
14865             ]
14866         }
14867         ]
14868     },
14869     
14870     content : {
14871         tag: 'tbody',
14872         cn: [
14873         {
14874             tag: 'tr',
14875             cn: [
14876             {
14877                 tag: 'td',
14878                 colspan: '7'
14879             }
14880             ]
14881         }
14882         ]
14883     },
14884     
14885     footer : {
14886         tag: 'tfoot',
14887         cn: [
14888         {
14889             tag: 'tr',
14890             cn: [
14891             {
14892                 tag: 'th',
14893                 colspan: '7',
14894                 cls: 'today'
14895             }
14896                     
14897             ]
14898         }
14899         ]
14900     },
14901     
14902     dates:{
14903         en: {
14904             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
14905             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
14906             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
14907             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
14908             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
14909             today: "Today"
14910         }
14911     },
14912     
14913     modes: [
14914     {
14915         clsName: 'days',
14916         navFnc: 'Month',
14917         navStep: 1
14918     },
14919     {
14920         clsName: 'months',
14921         navFnc: 'FullYear',
14922         navStep: 1
14923     },
14924     {
14925         clsName: 'years',
14926         navFnc: 'FullYear',
14927         navStep: 10
14928     }]
14929 });
14930
14931 Roo.apply(Roo.bootstrap.DateField,  {
14932   
14933     template : {
14934         tag: 'div',
14935         cls: 'datepicker dropdown-menu',
14936         cn: [
14937         {
14938             tag: 'div',
14939             cls: 'datepicker-days',
14940             cn: [
14941             {
14942                 tag: 'table',
14943                 cls: 'table-condensed',
14944                 cn:[
14945                 Roo.bootstrap.DateField.head,
14946                 {
14947                     tag: 'tbody'
14948                 },
14949                 Roo.bootstrap.DateField.footer
14950                 ]
14951             }
14952             ]
14953         },
14954         {
14955             tag: 'div',
14956             cls: 'datepicker-months',
14957             cn: [
14958             {
14959                 tag: 'table',
14960                 cls: 'table-condensed',
14961                 cn:[
14962                 Roo.bootstrap.DateField.head,
14963                 Roo.bootstrap.DateField.content,
14964                 Roo.bootstrap.DateField.footer
14965                 ]
14966             }
14967             ]
14968         },
14969         {
14970             tag: 'div',
14971             cls: 'datepicker-years',
14972             cn: [
14973             {
14974                 tag: 'table',
14975                 cls: 'table-condensed',
14976                 cn:[
14977                 Roo.bootstrap.DateField.head,
14978                 Roo.bootstrap.DateField.content,
14979                 Roo.bootstrap.DateField.footer
14980                 ]
14981             }
14982             ]
14983         }
14984         ]
14985     }
14986 });
14987
14988  
14989
14990  /*
14991  * - LGPL
14992  *
14993  * TimeField
14994  * 
14995  */
14996
14997 /**
14998  * @class Roo.bootstrap.TimeField
14999  * @extends Roo.bootstrap.Input
15000  * Bootstrap DateField class
15001  * 
15002  * 
15003  * @constructor
15004  * Create a new TimeField
15005  * @param {Object} config The config object
15006  */
15007
15008 Roo.bootstrap.TimeField = function(config){
15009     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
15010     this.addEvents({
15011             /**
15012              * @event show
15013              * Fires when this field show.
15014              * @param {Roo.bootstrap.DateField} this
15015              * @param {Mixed} date The date value
15016              */
15017             show : true,
15018             /**
15019              * @event show
15020              * Fires when this field hide.
15021              * @param {Roo.bootstrap.DateField} this
15022              * @param {Mixed} date The date value
15023              */
15024             hide : true,
15025             /**
15026              * @event select
15027              * Fires when select a date.
15028              * @param {Roo.bootstrap.DateField} this
15029              * @param {Mixed} date The date value
15030              */
15031             select : true
15032         });
15033 };
15034
15035 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
15036     
15037     /**
15038      * @cfg {String} format
15039      * The default time format string which can be overriden for localization support.  The format must be
15040      * valid according to {@link Date#parseDate} (defaults to 'H:i').
15041      */
15042     format : "H:i",
15043        
15044     onRender: function(ct, position)
15045     {
15046         
15047         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
15048                 
15049         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
15050         
15051         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15052         
15053         this.pop = this.picker().select('>.datepicker-time',true).first();
15054         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block' 
15055         
15056         this.picker().on('mousedown', this.onMousedown, this);
15057         this.picker().on('click', this.onClick, this);
15058         
15059         this.picker().addClass('datepicker-dropdown');
15060     
15061         this.fillTime();
15062         this.update();
15063             
15064         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
15065         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
15066         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
15067         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
15068         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
15069         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
15070
15071     },
15072     
15073     fireKey: function(e){
15074         if (!this.picker().isVisible()){
15075             if (e.keyCode == 27) // allow escape to hide and re-show picker
15076                 this.show();
15077             return;
15078         }
15079
15080         e.preventDefault();
15081         
15082         switch(e.keyCode){
15083             case 27: // escape
15084                 this.hide();
15085                 break;
15086             case 37: // left
15087             case 39: // right
15088                 this.onTogglePeriod();
15089                 break;
15090             case 38: // up
15091                 this.onIncrementMinutes();
15092                 break;
15093             case 40: // down
15094                 this.onDecrementMinutes();
15095                 break;
15096             case 13: // enter
15097             case 9: // tab
15098                 this.setTime();
15099                 break;
15100         }
15101     },
15102     
15103     onClick: function(e) {
15104         e.stopPropagation();
15105         e.preventDefault();
15106     },
15107     
15108     picker : function()
15109     {
15110         return this.el.select('.datepicker', true).first();
15111     },
15112     
15113     fillTime: function()
15114     {    
15115         var time = this.pop.select('tbody', true).first();
15116         
15117         time.dom.innerHTML = '';
15118         
15119         time.createChild({
15120             tag: 'tr',
15121             cn: [
15122                 {
15123                     tag: 'td',
15124                     cn: [
15125                         {
15126                             tag: 'a',
15127                             href: '#',
15128                             cls: 'btn',
15129                             cn: [
15130                                 {
15131                                     tag: 'span',
15132                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
15133                                 }
15134                             ]
15135                         } 
15136                     ]
15137                 },
15138                 {
15139                     tag: 'td',
15140                     cls: 'separator'
15141                 },
15142                 {
15143                     tag: 'td',
15144                     cn: [
15145                         {
15146                             tag: 'a',
15147                             href: '#',
15148                             cls: 'btn',
15149                             cn: [
15150                                 {
15151                                     tag: 'span',
15152                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
15153                                 }
15154                             ]
15155                         }
15156                     ]
15157                 },
15158                 {
15159                     tag: 'td',
15160                     cls: 'separator'
15161                 }
15162             ]
15163         });
15164         
15165         time.createChild({
15166             tag: 'tr',
15167             cn: [
15168                 {
15169                     tag: 'td',
15170                     cn: [
15171                         {
15172                             tag: 'span',
15173                             cls: 'timepicker-hour',
15174                             html: '00'
15175                         }  
15176                     ]
15177                 },
15178                 {
15179                     tag: 'td',
15180                     cls: 'separator',
15181                     html: ':'
15182                 },
15183                 {
15184                     tag: 'td',
15185                     cn: [
15186                         {
15187                             tag: 'span',
15188                             cls: 'timepicker-minute',
15189                             html: '00'
15190                         }  
15191                     ]
15192                 },
15193                 {
15194                     tag: 'td',
15195                     cls: 'separator'
15196                 },
15197                 {
15198                     tag: 'td',
15199                     cn: [
15200                         {
15201                             tag: 'button',
15202                             type: 'button',
15203                             cls: 'btn btn-primary period',
15204                             html: 'AM'
15205                             
15206                         }
15207                     ]
15208                 }
15209             ]
15210         });
15211         
15212         time.createChild({
15213             tag: 'tr',
15214             cn: [
15215                 {
15216                     tag: 'td',
15217                     cn: [
15218                         {
15219                             tag: 'a',
15220                             href: '#',
15221                             cls: 'btn',
15222                             cn: [
15223                                 {
15224                                     tag: 'span',
15225                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
15226                                 }
15227                             ]
15228                         }
15229                     ]
15230                 },
15231                 {
15232                     tag: 'td',
15233                     cls: 'separator'
15234                 },
15235                 {
15236                     tag: 'td',
15237                     cn: [
15238                         {
15239                             tag: 'a',
15240                             href: '#',
15241                             cls: 'btn',
15242                             cn: [
15243                                 {
15244                                     tag: 'span',
15245                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
15246                                 }
15247                             ]
15248                         }
15249                     ]
15250                 },
15251                 {
15252                     tag: 'td',
15253                     cls: 'separator'
15254                 }
15255             ]
15256         });
15257         
15258     },
15259     
15260     update: function()
15261     {
15262         
15263         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
15264         
15265         this.fill();
15266     },
15267     
15268     fill: function() 
15269     {
15270         var hours = this.time.getHours();
15271         var minutes = this.time.getMinutes();
15272         var period = 'AM';
15273         
15274         if(hours > 11){
15275             period = 'PM';
15276         }
15277         
15278         if(hours == 0){
15279             hours = 12;
15280         }
15281         
15282         
15283         if(hours > 12){
15284             hours = hours - 12;
15285         }
15286         
15287         if(hours < 10){
15288             hours = '0' + hours;
15289         }
15290         
15291         if(minutes < 10){
15292             minutes = '0' + minutes;
15293         }
15294         
15295         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
15296         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
15297         this.pop.select('button', true).first().dom.innerHTML = period;
15298         
15299     },
15300     
15301     place: function()
15302     {   
15303         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
15304         
15305         var cls = ['bottom'];
15306         
15307         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
15308             cls.pop();
15309             cls.push('top');
15310         }
15311         
15312         cls.push('right');
15313         
15314         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
15315             cls.pop();
15316             cls.push('left');
15317         }
15318         
15319         this.picker().addClass(cls.join('-'));
15320         
15321         var _this = this;
15322         
15323         Roo.each(cls, function(c){
15324             if(c == 'bottom'){
15325                 _this.picker().setTop(_this.inputEl().getHeight());
15326                 return;
15327             }
15328             if(c == 'top'){
15329                 _this.picker().setTop(0 - _this.picker().getHeight());
15330                 return;
15331             }
15332             
15333             if(c == 'left'){
15334                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
15335                 return;
15336             }
15337             if(c == 'right'){
15338                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
15339                 return;
15340             }
15341         });
15342         
15343     },
15344   
15345     onFocus : function()
15346     {
15347         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
15348         this.show();
15349     },
15350     
15351     onBlur : function()
15352     {
15353         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
15354         this.hide();
15355     },
15356     
15357     show : function()
15358     {
15359         this.picker().show();
15360         this.pop.show();
15361         this.update();
15362         this.place();
15363         
15364         this.fireEvent('show', this, this.date);
15365     },
15366     
15367     hide : function()
15368     {
15369         this.picker().hide();
15370         this.pop.hide();
15371         
15372         this.fireEvent('hide', this, this.date);
15373     },
15374     
15375     setTime : function()
15376     {
15377         this.hide();
15378         this.setValue(this.time.format(this.format));
15379         
15380         this.fireEvent('select', this, this.date);
15381         
15382         
15383     },
15384     
15385     onMousedown: function(e){
15386         e.stopPropagation();
15387         e.preventDefault();
15388     },
15389     
15390     onIncrementHours: function()
15391     {
15392         Roo.log('onIncrementHours');
15393         this.time = this.time.add(Date.HOUR, 1);
15394         this.update();
15395         
15396     },
15397     
15398     onDecrementHours: function()
15399     {
15400         Roo.log('onDecrementHours');
15401         this.time = this.time.add(Date.HOUR, -1);
15402         this.update();
15403     },
15404     
15405     onIncrementMinutes: function()
15406     {
15407         Roo.log('onIncrementMinutes');
15408         this.time = this.time.add(Date.MINUTE, 1);
15409         this.update();
15410     },
15411     
15412     onDecrementMinutes: function()
15413     {
15414         Roo.log('onDecrementMinutes');
15415         this.time = this.time.add(Date.MINUTE, -1);
15416         this.update();
15417     },
15418     
15419     onTogglePeriod: function()
15420     {
15421         Roo.log('onTogglePeriod');
15422         this.time = this.time.add(Date.HOUR, 12);
15423         this.update();
15424     }
15425     
15426    
15427 });
15428
15429 Roo.apply(Roo.bootstrap.TimeField,  {
15430     
15431     content : {
15432         tag: 'tbody',
15433         cn: [
15434             {
15435                 tag: 'tr',
15436                 cn: [
15437                 {
15438                     tag: 'td',
15439                     colspan: '7'
15440                 }
15441                 ]
15442             }
15443         ]
15444     },
15445     
15446     footer : {
15447         tag: 'tfoot',
15448         cn: [
15449             {
15450                 tag: 'tr',
15451                 cn: [
15452                 {
15453                     tag: 'th',
15454                     colspan: '7',
15455                     cls: '',
15456                     cn: [
15457                         {
15458                             tag: 'button',
15459                             cls: 'btn btn-info ok',
15460                             html: 'OK'
15461                         }
15462                     ]
15463                 }
15464
15465                 ]
15466             }
15467         ]
15468     }
15469 });
15470
15471 Roo.apply(Roo.bootstrap.TimeField,  {
15472   
15473     template : {
15474         tag: 'div',
15475         cls: 'datepicker dropdown-menu',
15476         cn: [
15477             {
15478                 tag: 'div',
15479                 cls: 'datepicker-time',
15480                 cn: [
15481                 {
15482                     tag: 'table',
15483                     cls: 'table-condensed',
15484                     cn:[
15485                     Roo.bootstrap.TimeField.content,
15486                     Roo.bootstrap.TimeField.footer
15487                     ]
15488                 }
15489                 ]
15490             }
15491         ]
15492     }
15493 });
15494
15495  
15496
15497  /*
15498  * - LGPL
15499  *
15500  * CheckBox
15501  * 
15502  */
15503
15504 /**
15505  * @class Roo.bootstrap.CheckBox
15506  * @extends Roo.bootstrap.Input
15507  * Bootstrap CheckBox class
15508  * 
15509  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
15510  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
15511  * @cfg {String} boxLabel The text that appears beside the checkbox
15512  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
15513  * @cfg {Boolean} checked initnal the element
15514  * 
15515  * 
15516  * @constructor
15517  * Create a new CheckBox
15518  * @param {Object} config The config object
15519  */
15520
15521 Roo.bootstrap.CheckBox = function(config){
15522     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
15523    
15524         this.addEvents({
15525             /**
15526             * @event check
15527             * Fires when the element is checked or unchecked.
15528             * @param {Roo.bootstrap.CheckBox} this This input
15529             * @param {Boolean} checked The new checked value
15530             */
15531            check : true
15532         });
15533 };
15534
15535 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
15536     
15537     inputType: 'checkbox',
15538     inputValue: 1,
15539     valueOff: 0,
15540     boxLabel: false,
15541     checked: false,
15542     weight : false,
15543     
15544     getAutoCreate : function()
15545     {
15546         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
15547         
15548         var id = Roo.id();
15549         
15550         var cfg = {};
15551         
15552         cfg.cls = 'form-group checkbox' //input-group
15553         
15554         
15555         
15556         
15557         var input =  {
15558             tag: 'input',
15559             id : id,
15560             type : this.inputType,
15561             value : (!this.checked) ? this.valueOff : this.inputValue,
15562             cls : 'roo-checkbox', //'form-box',
15563             placeholder : this.placeholder || ''
15564             
15565         };
15566         
15567         if (this.weight) { // Validity check?
15568             cfg.cls += " checkbox-" + this.weight;
15569         }
15570         
15571         if (this.disabled) {
15572             input.disabled=true;
15573         }
15574         
15575         if(this.checked){
15576             input.checked = this.checked;
15577         }
15578         
15579         if (this.name) {
15580             input.name = this.name;
15581         }
15582         
15583         if (this.size) {
15584             input.cls += ' input-' + this.size;
15585         }
15586         
15587         var settings=this;
15588         ['xs','sm','md','lg'].map(function(size){
15589             if (settings[size]) {
15590                 cfg.cls += ' col-' + size + '-' + settings[size];
15591             }
15592         });
15593         
15594        
15595         
15596         var inputblock = input;
15597         
15598         
15599         
15600         
15601         if (this.before || this.after) {
15602             
15603             inputblock = {
15604                 cls : 'input-group',
15605                 cn :  [] 
15606             };
15607             if (this.before) {
15608                 inputblock.cn.push({
15609                     tag :'span',
15610                     cls : 'input-group-addon',
15611                     html : this.before
15612                 });
15613             }
15614             inputblock.cn.push(input);
15615             if (this.after) {
15616                 inputblock.cn.push({
15617                     tag :'span',
15618                     cls : 'input-group-addon',
15619                     html : this.after
15620                 });
15621             }
15622             
15623         };
15624         
15625         if (align ==='left' && this.fieldLabel.length) {
15626                 Roo.log("left and has label");
15627                 cfg.cn = [
15628                     
15629                     {
15630                         tag: 'label',
15631                         'for' :  id,
15632                         cls : 'control-label col-md-' + this.labelWidth,
15633                         html : this.fieldLabel
15634                         
15635                     },
15636                     {
15637                         cls : "col-md-" + (12 - this.labelWidth), 
15638                         cn: [
15639                             inputblock
15640                         ]
15641                     }
15642                     
15643                 ];
15644         } else if ( this.fieldLabel.length) {
15645                 Roo.log(" label");
15646                 cfg.cn = [
15647                    
15648                     {
15649                         tag: this.boxLabel ? 'span' : 'label',
15650                         'for': id,
15651                         cls: 'control-label box-input-label',
15652                         //cls : 'input-group-addon',
15653                         html : this.fieldLabel
15654                         
15655                     },
15656                     
15657                     inputblock
15658                     
15659                 ];
15660
15661         } else {
15662             
15663                 Roo.log(" no label && no align");
15664                 cfg.cn = [  inputblock ] ;
15665                 
15666                 
15667         };
15668          if(this.boxLabel){
15669             cfg.cn.push( {
15670                 tag: 'label',
15671                 'for': id,
15672                 cls: 'box-label',
15673                 html: this.boxLabel
15674                 
15675             });
15676         }
15677         
15678         
15679        
15680         return cfg;
15681         
15682     },
15683     
15684     /**
15685      * return the real input element.
15686      */
15687     inputEl: function ()
15688     {
15689         return this.el.select('input.roo-checkbox',true).first();
15690     },
15691     
15692     label: function()
15693     {
15694         return this.el.select('label.control-label',true).first();
15695     },
15696     
15697     initEvents : function()
15698     {
15699 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
15700         
15701         this.inputEl().on('click', this.onClick,  this);
15702         
15703     },
15704     
15705     onClick : function()
15706     {   
15707         this.setChecked(!this.checked);
15708     },
15709     
15710     setChecked : function(state,suppressEvent)
15711     {
15712         this.checked = state;
15713         
15714         this.inputEl().dom.checked = state;
15715         
15716         if(suppressEvent !== true){
15717             this.fireEvent('check', this, state);
15718         }
15719         
15720         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
15721         
15722     },
15723     
15724     setValue : function(v,suppressEvent)
15725     {
15726         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
15727     }
15728     
15729 });
15730
15731  
15732 /*
15733  * - LGPL
15734  *
15735  * Radio
15736  * 
15737  */
15738
15739 /**
15740  * @class Roo.bootstrap.Radio
15741  * @extends Roo.bootstrap.CheckBox
15742  * Bootstrap Radio class
15743
15744  * @constructor
15745  * Create a new Radio
15746  * @param {Object} config The config object
15747  */
15748
15749 Roo.bootstrap.Radio = function(config){
15750     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
15751    
15752 };
15753
15754 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
15755     
15756     inputType: 'radio',
15757     inputValue: '',
15758     valueOff: '',
15759     
15760     getAutoCreate : function()
15761     {
15762         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
15763         
15764         var id = Roo.id();
15765         
15766         var cfg = {};
15767         
15768         cfg.cls = 'form-group radio' //input-group
15769         
15770         var input =  {
15771             tag: 'input',
15772             id : id,
15773             type : this.inputType,
15774             value : (!this.checked) ? this.valueOff : this.inputValue,
15775             cls : 'roo-radio',
15776             placeholder : this.placeholder || ''
15777             
15778         };
15779           if (this.weight) { // Validity check?
15780             cfg.cls += " radio-" + this.weight;
15781         }
15782         if (this.disabled) {
15783             input.disabled=true;
15784         }
15785         
15786         if(this.checked){
15787             input.checked = this.checked;
15788         }
15789         
15790         if (this.name) {
15791             input.name = this.name;
15792         }
15793         
15794         if (this.size) {
15795             input.cls += ' input-' + this.size;
15796         }
15797         
15798         var settings=this;
15799         ['xs','sm','md','lg'].map(function(size){
15800             if (settings[size]) {
15801                 cfg.cls += ' col-' + size + '-' + settings[size];
15802             }
15803         });
15804         
15805         var inputblock = input;
15806         
15807         if (this.before || this.after) {
15808             
15809             inputblock = {
15810                 cls : 'input-group',
15811                 cn :  [] 
15812             };
15813             if (this.before) {
15814                 inputblock.cn.push({
15815                     tag :'span',
15816                     cls : 'input-group-addon',
15817                     html : this.before
15818                 });
15819             }
15820             inputblock.cn.push(input);
15821             if (this.after) {
15822                 inputblock.cn.push({
15823                     tag :'span',
15824                     cls : 'input-group-addon',
15825                     html : this.after
15826                 });
15827             }
15828             
15829         };
15830         
15831         if (align ==='left' && this.fieldLabel.length) {
15832                 Roo.log("left and has label");
15833                 cfg.cn = [
15834                     
15835                     {
15836                         tag: 'label',
15837                         'for' :  id,
15838                         cls : 'control-label col-md-' + this.labelWidth,
15839                         html : this.fieldLabel
15840                         
15841                     },
15842                     {
15843                         cls : "col-md-" + (12 - this.labelWidth), 
15844                         cn: [
15845                             inputblock
15846                         ]
15847                     }
15848                     
15849                 ];
15850         } else if ( this.fieldLabel.length) {
15851                 Roo.log(" label");
15852                  cfg.cn = [
15853                    
15854                     {
15855                         tag: 'label',
15856                         'for': id,
15857                         cls: 'control-label box-input-label',
15858                         //cls : 'input-group-addon',
15859                         html : this.fieldLabel
15860                         
15861                     },
15862                     
15863                     inputblock
15864                     
15865                 ];
15866
15867         } else {
15868             
15869                    Roo.log(" no label && no align");
15870                 cfg.cn = [
15871                     
15872                         inputblock
15873                     
15874                 ];
15875                 
15876                 
15877         };
15878         
15879         if(this.boxLabel){
15880             cfg.cn.push({
15881                 tag: 'label',
15882                 'for': id,
15883                 cls: 'box-label',
15884                 html: this.boxLabel
15885             })
15886         }
15887         
15888         return cfg;
15889         
15890     },
15891     inputEl: function ()
15892     {
15893         return this.el.select('input.roo-radio',true).first();
15894     },
15895     onClick : function()
15896     {   
15897         this.setChecked(true);
15898     },
15899     
15900     setChecked : function(state,suppressEvent)
15901     {
15902         if(state){
15903             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
15904                 v.dom.checked = false;
15905             });
15906         }
15907         
15908         this.checked = state;
15909         this.inputEl().dom.checked = state;
15910         
15911         if(suppressEvent !== true){
15912             this.fireEvent('check', this, state);
15913         }
15914         
15915         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
15916         
15917     },
15918     
15919     getGroupValue : function()
15920     {
15921         var value = ''
15922         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
15923             if(v.dom.checked == true){
15924                 value = v.dom.value;
15925             }
15926         });
15927         
15928         return value;
15929     },
15930     
15931     /**
15932      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
15933      * @return {Mixed} value The field value
15934      */
15935     getValue : function(){
15936         return this.getGroupValue();
15937     }
15938     
15939 });
15940
15941  
15942 //<script type="text/javascript">
15943
15944 /*
15945  * Based  Ext JS Library 1.1.1
15946  * Copyright(c) 2006-2007, Ext JS, LLC.
15947  * LGPL
15948  *
15949  */
15950  
15951 /**
15952  * @class Roo.HtmlEditorCore
15953  * @extends Roo.Component
15954  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
15955  *
15956  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
15957  */
15958
15959 Roo.HtmlEditorCore = function(config){
15960     
15961     
15962     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
15963     this.addEvents({
15964         /**
15965          * @event initialize
15966          * Fires when the editor is fully initialized (including the iframe)
15967          * @param {Roo.HtmlEditorCore} this
15968          */
15969         initialize: true,
15970         /**
15971          * @event activate
15972          * Fires when the editor is first receives the focus. Any insertion must wait
15973          * until after this event.
15974          * @param {Roo.HtmlEditorCore} this
15975          */
15976         activate: true,
15977          /**
15978          * @event beforesync
15979          * Fires before the textarea is updated with content from the editor iframe. Return false
15980          * to cancel the sync.
15981          * @param {Roo.HtmlEditorCore} this
15982          * @param {String} html
15983          */
15984         beforesync: true,
15985          /**
15986          * @event beforepush
15987          * Fires before the iframe editor is updated with content from the textarea. Return false
15988          * to cancel the push.
15989          * @param {Roo.HtmlEditorCore} this
15990          * @param {String} html
15991          */
15992         beforepush: true,
15993          /**
15994          * @event sync
15995          * Fires when the textarea is updated with content from the editor iframe.
15996          * @param {Roo.HtmlEditorCore} this
15997          * @param {String} html
15998          */
15999         sync: true,
16000          /**
16001          * @event push
16002          * Fires when the iframe editor is updated with content from the textarea.
16003          * @param {Roo.HtmlEditorCore} this
16004          * @param {String} html
16005          */
16006         push: true,
16007         
16008         /**
16009          * @event editorevent
16010          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
16011          * @param {Roo.HtmlEditorCore} this
16012          */
16013         editorevent: true
16014     });
16015      
16016 };
16017
16018
16019 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
16020
16021
16022      /**
16023      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
16024      */
16025     
16026     owner : false,
16027     
16028      /**
16029      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
16030      *                        Roo.resizable.
16031      */
16032     resizable : false,
16033      /**
16034      * @cfg {Number} height (in pixels)
16035      */   
16036     height: 300,
16037    /**
16038      * @cfg {Number} width (in pixels)
16039      */   
16040     width: 500,
16041     
16042     /**
16043      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
16044      * 
16045      */
16046     stylesheets: false,
16047     
16048     // id of frame..
16049     frameId: false,
16050     
16051     // private properties
16052     validationEvent : false,
16053     deferHeight: true,
16054     initialized : false,
16055     activated : false,
16056     sourceEditMode : false,
16057     onFocus : Roo.emptyFn,
16058     iframePad:3,
16059     hideMode:'offsets',
16060     
16061     clearUp: true,
16062     
16063      
16064     
16065
16066     /**
16067      * Protected method that will not generally be called directly. It
16068      * is called when the editor initializes the iframe with HTML contents. Override this method if you
16069      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
16070      */
16071     getDocMarkup : function(){
16072         // body styles..
16073         var st = '';
16074         Roo.log(this.stylesheets);
16075         
16076         // inherit styels from page...?? 
16077         if (this.stylesheets === false) {
16078             
16079             Roo.get(document.head).select('style').each(function(node) {
16080                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16081             });
16082             
16083             Roo.get(document.head).select('link').each(function(node) { 
16084                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16085             });
16086             
16087         } else if (!this.stylesheets.length) {
16088                 // simple..
16089                 st = '<style type="text/css">' +
16090                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16091                    '</style>';
16092         } else {
16093             Roo.each(this.stylesheets, function(s) {
16094                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
16095             });
16096             
16097         }
16098         
16099         st +=  '<style type="text/css">' +
16100             'IMG { cursor: pointer } ' +
16101         '</style>';
16102
16103         
16104         return '<html><head>' + st  +
16105             //<style type="text/css">' +
16106             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16107             //'</style>' +
16108             ' </head><body class="roo-htmleditor-body"></body></html>';
16109     },
16110
16111     // private
16112     onRender : function(ct, position)
16113     {
16114         var _t = this;
16115         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
16116         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
16117         
16118         
16119         this.el.dom.style.border = '0 none';
16120         this.el.dom.setAttribute('tabIndex', -1);
16121         this.el.addClass('x-hidden hide');
16122         
16123         
16124         
16125         if(Roo.isIE){ // fix IE 1px bogus margin
16126             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
16127         }
16128        
16129         
16130         this.frameId = Roo.id();
16131         
16132          
16133         
16134         var iframe = this.owner.wrap.createChild({
16135             tag: 'iframe',
16136             cls: 'form-control', // bootstrap..
16137             id: this.frameId,
16138             name: this.frameId,
16139             frameBorder : 'no',
16140             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
16141         }, this.el
16142         );
16143         
16144         
16145         this.iframe = iframe.dom;
16146
16147          this.assignDocWin();
16148         
16149         this.doc.designMode = 'on';
16150        
16151         this.doc.open();
16152         this.doc.write(this.getDocMarkup());
16153         this.doc.close();
16154
16155         
16156         var task = { // must defer to wait for browser to be ready
16157             run : function(){
16158                 //console.log("run task?" + this.doc.readyState);
16159                 this.assignDocWin();
16160                 if(this.doc.body || this.doc.readyState == 'complete'){
16161                     try {
16162                         this.doc.designMode="on";
16163                     } catch (e) {
16164                         return;
16165                     }
16166                     Roo.TaskMgr.stop(task);
16167                     this.initEditor.defer(10, this);
16168                 }
16169             },
16170             interval : 10,
16171             duration: 10000,
16172             scope: this
16173         };
16174         Roo.TaskMgr.start(task);
16175
16176         
16177          
16178     },
16179
16180     // private
16181     onResize : function(w, h)
16182     {
16183          Roo.log('resize: ' +w + ',' + h );
16184         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
16185         if(!this.iframe){
16186             return;
16187         }
16188         if(typeof w == 'number'){
16189             
16190             this.iframe.style.width = w + 'px';
16191         }
16192         if(typeof h == 'number'){
16193             
16194             this.iframe.style.height = h + 'px';
16195             if(this.doc){
16196                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
16197             }
16198         }
16199         
16200     },
16201
16202     /**
16203      * Toggles the editor between standard and source edit mode.
16204      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
16205      */
16206     toggleSourceEdit : function(sourceEditMode){
16207         
16208         this.sourceEditMode = sourceEditMode === true;
16209         
16210         if(this.sourceEditMode){
16211  
16212             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
16213             
16214         }else{
16215             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
16216             //this.iframe.className = '';
16217             this.deferFocus();
16218         }
16219         //this.setSize(this.owner.wrap.getSize());
16220         //this.fireEvent('editmodechange', this, this.sourceEditMode);
16221     },
16222
16223     
16224   
16225
16226     /**
16227      * Protected method that will not generally be called directly. If you need/want
16228      * custom HTML cleanup, this is the method you should override.
16229      * @param {String} html The HTML to be cleaned
16230      * return {String} The cleaned HTML
16231      */
16232     cleanHtml : function(html){
16233         html = String(html);
16234         if(html.length > 5){
16235             if(Roo.isSafari){ // strip safari nonsense
16236                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
16237             }
16238         }
16239         if(html == '&nbsp;'){
16240             html = '';
16241         }
16242         return html;
16243     },
16244
16245     /**
16246      * HTML Editor -> Textarea
16247      * Protected method that will not generally be called directly. Syncs the contents
16248      * of the editor iframe with the textarea.
16249      */
16250     syncValue : function(){
16251         if(this.initialized){
16252             var bd = (this.doc.body || this.doc.documentElement);
16253             //this.cleanUpPaste(); -- this is done else where and causes havoc..
16254             var html = bd.innerHTML;
16255             if(Roo.isSafari){
16256                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
16257                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
16258                 if(m && m[1]){
16259                     html = '<div style="'+m[0]+'">' + html + '</div>';
16260                 }
16261             }
16262             html = this.cleanHtml(html);
16263             // fix up the special chars.. normaly like back quotes in word...
16264             // however we do not want to do this with chinese..
16265             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
16266                 var cc = b.charCodeAt();
16267                 if (
16268                     (cc >= 0x4E00 && cc < 0xA000 ) ||
16269                     (cc >= 0x3400 && cc < 0x4E00 ) ||
16270                     (cc >= 0xf900 && cc < 0xfb00 )
16271                 ) {
16272                         return b;
16273                 }
16274                 return "&#"+cc+";" 
16275             });
16276             if(this.owner.fireEvent('beforesync', this, html) !== false){
16277                 this.el.dom.value = html;
16278                 this.owner.fireEvent('sync', this, html);
16279             }
16280         }
16281     },
16282
16283     /**
16284      * Protected method that will not generally be called directly. Pushes the value of the textarea
16285      * into the iframe editor.
16286      */
16287     pushValue : function(){
16288         if(this.initialized){
16289             var v = this.el.dom.value.trim();
16290             
16291 //            if(v.length < 1){
16292 //                v = '&#160;';
16293 //            }
16294             
16295             if(this.owner.fireEvent('beforepush', this, v) !== false){
16296                 var d = (this.doc.body || this.doc.documentElement);
16297                 d.innerHTML = v;
16298                 this.cleanUpPaste();
16299                 this.el.dom.value = d.innerHTML;
16300                 this.owner.fireEvent('push', this, v);
16301             }
16302         }
16303     },
16304
16305     // private
16306     deferFocus : function(){
16307         this.focus.defer(10, this);
16308     },
16309
16310     // doc'ed in Field
16311     focus : function(){
16312         if(this.win && !this.sourceEditMode){
16313             this.win.focus();
16314         }else{
16315             this.el.focus();
16316         }
16317     },
16318     
16319     assignDocWin: function()
16320     {
16321         var iframe = this.iframe;
16322         
16323          if(Roo.isIE){
16324             this.doc = iframe.contentWindow.document;
16325             this.win = iframe.contentWindow;
16326         } else {
16327             if (!Roo.get(this.frameId)) {
16328                 return;
16329             }
16330             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
16331             this.win = Roo.get(this.frameId).dom.contentWindow;
16332         }
16333     },
16334     
16335     // private
16336     initEditor : function(){
16337         //console.log("INIT EDITOR");
16338         this.assignDocWin();
16339         
16340         
16341         
16342         this.doc.designMode="on";
16343         this.doc.open();
16344         this.doc.write(this.getDocMarkup());
16345         this.doc.close();
16346         
16347         var dbody = (this.doc.body || this.doc.documentElement);
16348         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
16349         // this copies styles from the containing element into thsi one..
16350         // not sure why we need all of this..
16351         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
16352         
16353         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
16354         //ss['background-attachment'] = 'fixed'; // w3c
16355         dbody.bgProperties = 'fixed'; // ie
16356         //Roo.DomHelper.applyStyles(dbody, ss);
16357         Roo.EventManager.on(this.doc, {
16358             //'mousedown': this.onEditorEvent,
16359             'mouseup': this.onEditorEvent,
16360             'dblclick': this.onEditorEvent,
16361             'click': this.onEditorEvent,
16362             'keyup': this.onEditorEvent,
16363             buffer:100,
16364             scope: this
16365         });
16366         if(Roo.isGecko){
16367             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
16368         }
16369         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
16370             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
16371         }
16372         this.initialized = true;
16373
16374         this.owner.fireEvent('initialize', this);
16375         this.pushValue();
16376     },
16377
16378     // private
16379     onDestroy : function(){
16380         
16381         
16382         
16383         if(this.rendered){
16384             
16385             //for (var i =0; i < this.toolbars.length;i++) {
16386             //    // fixme - ask toolbars for heights?
16387             //    this.toolbars[i].onDestroy();
16388            // }
16389             
16390             //this.wrap.dom.innerHTML = '';
16391             //this.wrap.remove();
16392         }
16393     },
16394
16395     // private
16396     onFirstFocus : function(){
16397         
16398         this.assignDocWin();
16399         
16400         
16401         this.activated = true;
16402          
16403     
16404         if(Roo.isGecko){ // prevent silly gecko errors
16405             this.win.focus();
16406             var s = this.win.getSelection();
16407             if(!s.focusNode || s.focusNode.nodeType != 3){
16408                 var r = s.getRangeAt(0);
16409                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
16410                 r.collapse(true);
16411                 this.deferFocus();
16412             }
16413             try{
16414                 this.execCmd('useCSS', true);
16415                 this.execCmd('styleWithCSS', false);
16416             }catch(e){}
16417         }
16418         this.owner.fireEvent('activate', this);
16419     },
16420
16421     // private
16422     adjustFont: function(btn){
16423         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
16424         //if(Roo.isSafari){ // safari
16425         //    adjust *= 2;
16426        // }
16427         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
16428         if(Roo.isSafari){ // safari
16429             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
16430             v =  (v < 10) ? 10 : v;
16431             v =  (v > 48) ? 48 : v;
16432             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
16433             
16434         }
16435         
16436         
16437         v = Math.max(1, v+adjust);
16438         
16439         this.execCmd('FontSize', v  );
16440     },
16441
16442     onEditorEvent : function(e){
16443         this.owner.fireEvent('editorevent', this, e);
16444       //  this.updateToolbar();
16445         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
16446     },
16447
16448     insertTag : function(tg)
16449     {
16450         // could be a bit smarter... -> wrap the current selected tRoo..
16451         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
16452             
16453             range = this.createRange(this.getSelection());
16454             var wrappingNode = this.doc.createElement(tg.toLowerCase());
16455             wrappingNode.appendChild(range.extractContents());
16456             range.insertNode(wrappingNode);
16457
16458             return;
16459             
16460             
16461             
16462         }
16463         this.execCmd("formatblock",   tg);
16464         
16465     },
16466     
16467     insertText : function(txt)
16468     {
16469         
16470         
16471         var range = this.createRange();
16472         range.deleteContents();
16473                //alert(Sender.getAttribute('label'));
16474                
16475         range.insertNode(this.doc.createTextNode(txt));
16476     } ,
16477     
16478      
16479
16480     /**
16481      * Executes a Midas editor command on the editor document and performs necessary focus and
16482      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
16483      * @param {String} cmd The Midas command
16484      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16485      */
16486     relayCmd : function(cmd, value){
16487         this.win.focus();
16488         this.execCmd(cmd, value);
16489         this.owner.fireEvent('editorevent', this);
16490         //this.updateToolbar();
16491         this.owner.deferFocus();
16492     },
16493
16494     /**
16495      * Executes a Midas editor command directly on the editor document.
16496      * For visual commands, you should use {@link #relayCmd} instead.
16497      * <b>This should only be called after the editor is initialized.</b>
16498      * @param {String} cmd The Midas command
16499      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16500      */
16501     execCmd : function(cmd, value){
16502         this.doc.execCommand(cmd, false, value === undefined ? null : value);
16503         this.syncValue();
16504     },
16505  
16506  
16507    
16508     /**
16509      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
16510      * to insert tRoo.
16511      * @param {String} text | dom node.. 
16512      */
16513     insertAtCursor : function(text)
16514     {
16515         
16516         
16517         
16518         if(!this.activated){
16519             return;
16520         }
16521         /*
16522         if(Roo.isIE){
16523             this.win.focus();
16524             var r = this.doc.selection.createRange();
16525             if(r){
16526                 r.collapse(true);
16527                 r.pasteHTML(text);
16528                 this.syncValue();
16529                 this.deferFocus();
16530             
16531             }
16532             return;
16533         }
16534         */
16535         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
16536             this.win.focus();
16537             
16538             
16539             // from jquery ui (MIT licenced)
16540             var range, node;
16541             var win = this.win;
16542             
16543             if (win.getSelection && win.getSelection().getRangeAt) {
16544                 range = win.getSelection().getRangeAt(0);
16545                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
16546                 range.insertNode(node);
16547             } else if (win.document.selection && win.document.selection.createRange) {
16548                 // no firefox support
16549                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
16550                 win.document.selection.createRange().pasteHTML(txt);
16551             } else {
16552                 // no firefox support
16553                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
16554                 this.execCmd('InsertHTML', txt);
16555             } 
16556             
16557             this.syncValue();
16558             
16559             this.deferFocus();
16560         }
16561     },
16562  // private
16563     mozKeyPress : function(e){
16564         if(e.ctrlKey){
16565             var c = e.getCharCode(), cmd;
16566           
16567             if(c > 0){
16568                 c = String.fromCharCode(c).toLowerCase();
16569                 switch(c){
16570                     case 'b':
16571                         cmd = 'bold';
16572                         break;
16573                     case 'i':
16574                         cmd = 'italic';
16575                         break;
16576                     
16577                     case 'u':
16578                         cmd = 'underline';
16579                         break;
16580                     
16581                     case 'v':
16582                         this.cleanUpPaste.defer(100, this);
16583                         return;
16584                         
16585                 }
16586                 if(cmd){
16587                     this.win.focus();
16588                     this.execCmd(cmd);
16589                     this.deferFocus();
16590                     e.preventDefault();
16591                 }
16592                 
16593             }
16594         }
16595     },
16596
16597     // private
16598     fixKeys : function(){ // load time branching for fastest keydown performance
16599         if(Roo.isIE){
16600             return function(e){
16601                 var k = e.getKey(), r;
16602                 if(k == e.TAB){
16603                     e.stopEvent();
16604                     r = this.doc.selection.createRange();
16605                     if(r){
16606                         r.collapse(true);
16607                         r.pasteHTML('&#160;&#160;&#160;&#160;');
16608                         this.deferFocus();
16609                     }
16610                     return;
16611                 }
16612                 
16613                 if(k == e.ENTER){
16614                     r = this.doc.selection.createRange();
16615                     if(r){
16616                         var target = r.parentElement();
16617                         if(!target || target.tagName.toLowerCase() != 'li'){
16618                             e.stopEvent();
16619                             r.pasteHTML('<br />');
16620                             r.collapse(false);
16621                             r.select();
16622                         }
16623                     }
16624                 }
16625                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
16626                     this.cleanUpPaste.defer(100, this);
16627                     return;
16628                 }
16629                 
16630                 
16631             };
16632         }else if(Roo.isOpera){
16633             return function(e){
16634                 var k = e.getKey();
16635                 if(k == e.TAB){
16636                     e.stopEvent();
16637                     this.win.focus();
16638                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
16639                     this.deferFocus();
16640                 }
16641                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
16642                     this.cleanUpPaste.defer(100, this);
16643                     return;
16644                 }
16645                 
16646             };
16647         }else if(Roo.isSafari){
16648             return function(e){
16649                 var k = e.getKey();
16650                 
16651                 if(k == e.TAB){
16652                     e.stopEvent();
16653                     this.execCmd('InsertText','\t');
16654                     this.deferFocus();
16655                     return;
16656                 }
16657                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
16658                     this.cleanUpPaste.defer(100, this);
16659                     return;
16660                 }
16661                 
16662              };
16663         }
16664     }(),
16665     
16666     getAllAncestors: function()
16667     {
16668         var p = this.getSelectedNode();
16669         var a = [];
16670         if (!p) {
16671             a.push(p); // push blank onto stack..
16672             p = this.getParentElement();
16673         }
16674         
16675         
16676         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
16677             a.push(p);
16678             p = p.parentNode;
16679         }
16680         a.push(this.doc.body);
16681         return a;
16682     },
16683     lastSel : false,
16684     lastSelNode : false,
16685     
16686     
16687     getSelection : function() 
16688     {
16689         this.assignDocWin();
16690         return Roo.isIE ? this.doc.selection : this.win.getSelection();
16691     },
16692     
16693     getSelectedNode: function() 
16694     {
16695         // this may only work on Gecko!!!
16696         
16697         // should we cache this!!!!
16698         
16699         
16700         
16701          
16702         var range = this.createRange(this.getSelection()).cloneRange();
16703         
16704         if (Roo.isIE) {
16705             var parent = range.parentElement();
16706             while (true) {
16707                 var testRange = range.duplicate();
16708                 testRange.moveToElementText(parent);
16709                 if (testRange.inRange(range)) {
16710                     break;
16711                 }
16712                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
16713                     break;
16714                 }
16715                 parent = parent.parentElement;
16716             }
16717             return parent;
16718         }
16719         
16720         // is ancestor a text element.
16721         var ac =  range.commonAncestorContainer;
16722         if (ac.nodeType == 3) {
16723             ac = ac.parentNode;
16724         }
16725         
16726         var ar = ac.childNodes;
16727          
16728         var nodes = [];
16729         var other_nodes = [];
16730         var has_other_nodes = false;
16731         for (var i=0;i<ar.length;i++) {
16732             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
16733                 continue;
16734             }
16735             // fullly contained node.
16736             
16737             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
16738                 nodes.push(ar[i]);
16739                 continue;
16740             }
16741             
16742             // probably selected..
16743             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
16744                 other_nodes.push(ar[i]);
16745                 continue;
16746             }
16747             // outer..
16748             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
16749                 continue;
16750             }
16751             
16752             
16753             has_other_nodes = true;
16754         }
16755         if (!nodes.length && other_nodes.length) {
16756             nodes= other_nodes;
16757         }
16758         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
16759             return false;
16760         }
16761         
16762         return nodes[0];
16763     },
16764     createRange: function(sel)
16765     {
16766         // this has strange effects when using with 
16767         // top toolbar - not sure if it's a great idea.
16768         //this.editor.contentWindow.focus();
16769         if (typeof sel != "undefined") {
16770             try {
16771                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
16772             } catch(e) {
16773                 return this.doc.createRange();
16774             }
16775         } else {
16776             return this.doc.createRange();
16777         }
16778     },
16779     getParentElement: function()
16780     {
16781         
16782         this.assignDocWin();
16783         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
16784         
16785         var range = this.createRange(sel);
16786          
16787         try {
16788             var p = range.commonAncestorContainer;
16789             while (p.nodeType == 3) { // text node
16790                 p = p.parentNode;
16791             }
16792             return p;
16793         } catch (e) {
16794             return null;
16795         }
16796     
16797     },
16798     /***
16799      *
16800      * Range intersection.. the hard stuff...
16801      *  '-1' = before
16802      *  '0' = hits..
16803      *  '1' = after.
16804      *         [ -- selected range --- ]
16805      *   [fail]                        [fail]
16806      *
16807      *    basically..
16808      *      if end is before start or  hits it. fail.
16809      *      if start is after end or hits it fail.
16810      *
16811      *   if either hits (but other is outside. - then it's not 
16812      *   
16813      *    
16814      **/
16815     
16816     
16817     // @see http://www.thismuchiknow.co.uk/?p=64.
16818     rangeIntersectsNode : function(range, node)
16819     {
16820         var nodeRange = node.ownerDocument.createRange();
16821         try {
16822             nodeRange.selectNode(node);
16823         } catch (e) {
16824             nodeRange.selectNodeContents(node);
16825         }
16826     
16827         var rangeStartRange = range.cloneRange();
16828         rangeStartRange.collapse(true);
16829     
16830         var rangeEndRange = range.cloneRange();
16831         rangeEndRange.collapse(false);
16832     
16833         var nodeStartRange = nodeRange.cloneRange();
16834         nodeStartRange.collapse(true);
16835     
16836         var nodeEndRange = nodeRange.cloneRange();
16837         nodeEndRange.collapse(false);
16838     
16839         return rangeStartRange.compareBoundaryPoints(
16840                  Range.START_TO_START, nodeEndRange) == -1 &&
16841                rangeEndRange.compareBoundaryPoints(
16842                  Range.START_TO_START, nodeStartRange) == 1;
16843         
16844          
16845     },
16846     rangeCompareNode : function(range, node)
16847     {
16848         var nodeRange = node.ownerDocument.createRange();
16849         try {
16850             nodeRange.selectNode(node);
16851         } catch (e) {
16852             nodeRange.selectNodeContents(node);
16853         }
16854         
16855         
16856         range.collapse(true);
16857     
16858         nodeRange.collapse(true);
16859      
16860         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
16861         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
16862          
16863         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
16864         
16865         var nodeIsBefore   =  ss == 1;
16866         var nodeIsAfter    = ee == -1;
16867         
16868         if (nodeIsBefore && nodeIsAfter)
16869             return 0; // outer
16870         if (!nodeIsBefore && nodeIsAfter)
16871             return 1; //right trailed.
16872         
16873         if (nodeIsBefore && !nodeIsAfter)
16874             return 2;  // left trailed.
16875         // fully contined.
16876         return 3;
16877     },
16878
16879     // private? - in a new class?
16880     cleanUpPaste :  function()
16881     {
16882         // cleans up the whole document..
16883         Roo.log('cleanuppaste');
16884         
16885         this.cleanUpChildren(this.doc.body);
16886         var clean = this.cleanWordChars(this.doc.body.innerHTML);
16887         if (clean != this.doc.body.innerHTML) {
16888             this.doc.body.innerHTML = clean;
16889         }
16890         
16891     },
16892     
16893     cleanWordChars : function(input) {// change the chars to hex code
16894         var he = Roo.HtmlEditorCore;
16895         
16896         var output = input;
16897         Roo.each(he.swapCodes, function(sw) { 
16898             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
16899             
16900             output = output.replace(swapper, sw[1]);
16901         });
16902         
16903         return output;
16904     },
16905     
16906     
16907     cleanUpChildren : function (n)
16908     {
16909         if (!n.childNodes.length) {
16910             return;
16911         }
16912         for (var i = n.childNodes.length-1; i > -1 ; i--) {
16913            this.cleanUpChild(n.childNodes[i]);
16914         }
16915     },
16916     
16917     
16918         
16919     
16920     cleanUpChild : function (node)
16921     {
16922         var ed = this;
16923         //console.log(node);
16924         if (node.nodeName == "#text") {
16925             // clean up silly Windows -- stuff?
16926             return; 
16927         }
16928         if (node.nodeName == "#comment") {
16929             node.parentNode.removeChild(node);
16930             // clean up silly Windows -- stuff?
16931             return; 
16932         }
16933         
16934         if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1 && this.clearUp) {
16935             // remove node.
16936             node.parentNode.removeChild(node);
16937             return;
16938             
16939         }
16940         
16941         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
16942         
16943         // remove <a name=....> as rendering on yahoo mailer is borked with this.
16944         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
16945         
16946         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
16947         //    remove_keep_children = true;
16948         //}
16949         
16950         if (remove_keep_children) {
16951             this.cleanUpChildren(node);
16952             // inserts everything just before this node...
16953             while (node.childNodes.length) {
16954                 var cn = node.childNodes[0];
16955                 node.removeChild(cn);
16956                 node.parentNode.insertBefore(cn, node);
16957             }
16958             node.parentNode.removeChild(node);
16959             return;
16960         }
16961         
16962         if (!node.attributes || !node.attributes.length) {
16963             this.cleanUpChildren(node);
16964             return;
16965         }
16966         
16967         function cleanAttr(n,v)
16968         {
16969             
16970             if (v.match(/^\./) || v.match(/^\//)) {
16971                 return;
16972             }
16973             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
16974                 return;
16975             }
16976             if (v.match(/^#/)) {
16977                 return;
16978             }
16979 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
16980             node.removeAttribute(n);
16981             
16982         }
16983         
16984         function cleanStyle(n,v)
16985         {
16986             if (v.match(/expression/)) { //XSS?? should we even bother..
16987                 node.removeAttribute(n);
16988                 return;
16989             }
16990             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
16991             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
16992             
16993             
16994             var parts = v.split(/;/);
16995             var clean = [];
16996             
16997             Roo.each(parts, function(p) {
16998                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
16999                 if (!p.length) {
17000                     return true;
17001                 }
17002                 var l = p.split(':').shift().replace(/\s+/g,'');
17003                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
17004                 
17005                 if ( cblack.indexOf(l) > -1) {
17006 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17007                     //node.removeAttribute(n);
17008                     return true;
17009                 }
17010                 //Roo.log()
17011                 // only allow 'c whitelisted system attributes'
17012                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
17013 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17014                     //node.removeAttribute(n);
17015                     return true;
17016                 }
17017                 
17018                 
17019                  
17020                 
17021                 clean.push(p);
17022                 return true;
17023             });
17024             if (clean.length) { 
17025                 node.setAttribute(n, clean.join(';'));
17026             } else {
17027                 node.removeAttribute(n);
17028             }
17029             
17030         }
17031         
17032         
17033         for (var i = node.attributes.length-1; i > -1 ; i--) {
17034             var a = node.attributes[i];
17035             //console.log(a);
17036             
17037             if (a.name.toLowerCase().substr(0,2)=='on')  {
17038                 node.removeAttribute(a.name);
17039                 continue;
17040             }
17041             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
17042                 node.removeAttribute(a.name);
17043                 continue;
17044             }
17045             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
17046                 cleanAttr(a.name,a.value); // fixme..
17047                 continue;
17048             }
17049             if (a.name == 'style') {
17050                 cleanStyle(a.name,a.value);
17051                 continue;
17052             }
17053             /// clean up MS crap..
17054             // tecnically this should be a list of valid class'es..
17055             
17056             
17057             if (a.name == 'class') {
17058                 if (a.value.match(/^Mso/)) {
17059                     node.className = '';
17060                 }
17061                 
17062                 if (a.value.match(/body/)) {
17063                     node.className = '';
17064                 }
17065                 continue;
17066             }
17067             
17068             // style cleanup!?
17069             // class cleanup?
17070             
17071         }
17072         
17073         
17074         this.cleanUpChildren(node);
17075         
17076         
17077     },
17078     /**
17079      * Clean up MS wordisms...
17080      */
17081     cleanWord : function(node)
17082     {
17083         var _t = this;
17084         var cleanWordChildren = function()
17085         {
17086             if (!node.childNodes.length) {
17087                 return;
17088             }
17089             for (var i = node.childNodes.length-1; i > -1 ; i--) {
17090                _t.cleanWord(node.childNodes[i]);
17091             }
17092         }
17093         
17094         
17095         if (!node) {
17096             this.cleanWord(this.doc.body);
17097             return;
17098         }
17099         if (node.nodeName == "#text") {
17100             // clean up silly Windows -- stuff?
17101             return; 
17102         }
17103         if (node.nodeName == "#comment") {
17104             node.parentNode.removeChild(node);
17105             // clean up silly Windows -- stuff?
17106             return; 
17107         }
17108         
17109         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
17110             node.parentNode.removeChild(node);
17111             return;
17112         }
17113         
17114         // remove - but keep children..
17115         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
17116             while (node.childNodes.length) {
17117                 var cn = node.childNodes[0];
17118                 node.removeChild(cn);
17119                 node.parentNode.insertBefore(cn, node);
17120             }
17121             node.parentNode.removeChild(node);
17122             cleanWordChildren();
17123             return;
17124         }
17125         // clean styles
17126         if (node.className.length) {
17127             
17128             var cn = node.className.split(/\W+/);
17129             var cna = [];
17130             Roo.each(cn, function(cls) {
17131                 if (cls.match(/Mso[a-zA-Z]+/)) {
17132                     return;
17133                 }
17134                 cna.push(cls);
17135             });
17136             node.className = cna.length ? cna.join(' ') : '';
17137             if (!cna.length) {
17138                 node.removeAttribute("class");
17139             }
17140         }
17141         
17142         if (node.hasAttribute("lang")) {
17143             node.removeAttribute("lang");
17144         }
17145         
17146         if (node.hasAttribute("style")) {
17147             
17148             var styles = node.getAttribute("style").split(";");
17149             var nstyle = [];
17150             Roo.each(styles, function(s) {
17151                 if (!s.match(/:/)) {
17152                     return;
17153                 }
17154                 var kv = s.split(":");
17155                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
17156                     return;
17157                 }
17158                 // what ever is left... we allow.
17159                 nstyle.push(s);
17160             });
17161             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
17162             if (!nstyle.length) {
17163                 node.removeAttribute('style');
17164             }
17165         }
17166         
17167         cleanWordChildren();
17168         
17169         
17170     },
17171     domToHTML : function(currentElement, depth, nopadtext) {
17172         
17173             depth = depth || 0;
17174             nopadtext = nopadtext || false;
17175         
17176             if (!currentElement) {
17177                 return this.domToHTML(this.doc.body);
17178             }
17179             
17180             //Roo.log(currentElement);
17181             var j;
17182             var allText = false;
17183             var nodeName = currentElement.nodeName;
17184             var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
17185             
17186             if  (nodeName == '#text') {
17187                 return currentElement.nodeValue;
17188             }
17189             
17190             
17191             var ret = '';
17192             if (nodeName != 'BODY') {
17193                  
17194                 var i = 0;
17195                 // Prints the node tagName, such as <A>, <IMG>, etc
17196                 if (tagName) {
17197                     var attr = [];
17198                     for(i = 0; i < currentElement.attributes.length;i++) {
17199                         // quoting?
17200                         var aname = currentElement.attributes.item(i).name;
17201                         if (!currentElement.attributes.item(i).value.length) {
17202                             continue;
17203                         }
17204                         attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
17205                     }
17206                     
17207                     ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
17208                 } 
17209                 else {
17210                     
17211                     // eack
17212                 }
17213             } else {
17214                 tagName = false;
17215             }
17216             if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
17217                 return ret;
17218             }
17219             if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
17220                 nopadtext = true;
17221             }
17222             
17223             
17224             // Traverse the tree
17225             i = 0;
17226             var currentElementChild = currentElement.childNodes.item(i);
17227             var allText = true;
17228             var innerHTML  = '';
17229             lastnode = '';
17230             while (currentElementChild) {
17231                 // Formatting code (indent the tree so it looks nice on the screen)
17232                 var nopad = nopadtext;
17233                 if (lastnode == 'SPAN') {
17234                     nopad  = true;
17235                 }
17236                 // text
17237                 if  (currentElementChild.nodeName == '#text') {
17238                     var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
17239                     if (!nopad && toadd.length > 80) {
17240                         innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
17241                     }
17242                     innerHTML  += toadd;
17243                     
17244                     i++;
17245                     currentElementChild = currentElement.childNodes.item(i);
17246                     lastNode = '';
17247                     continue;
17248                 }
17249                 allText = false;
17250                 
17251                 innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
17252                     
17253                 // Recursively traverse the tree structure of the child node
17254                 innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
17255                 lastnode = currentElementChild.nodeName;
17256                 i++;
17257                 currentElementChild=currentElement.childNodes.item(i);
17258             }
17259             
17260             ret += innerHTML;
17261             
17262             if (!allText) {
17263                     // The remaining code is mostly for formatting the tree
17264                 ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
17265             }
17266             
17267             
17268             if (tagName) {
17269                 ret+= "</"+tagName+">";
17270             }
17271             return ret;
17272             
17273         }
17274     
17275     // hide stuff that is not compatible
17276     /**
17277      * @event blur
17278      * @hide
17279      */
17280     /**
17281      * @event change
17282      * @hide
17283      */
17284     /**
17285      * @event focus
17286      * @hide
17287      */
17288     /**
17289      * @event specialkey
17290      * @hide
17291      */
17292     /**
17293      * @cfg {String} fieldClass @hide
17294      */
17295     /**
17296      * @cfg {String} focusClass @hide
17297      */
17298     /**
17299      * @cfg {String} autoCreate @hide
17300      */
17301     /**
17302      * @cfg {String} inputType @hide
17303      */
17304     /**
17305      * @cfg {String} invalidClass @hide
17306      */
17307     /**
17308      * @cfg {String} invalidText @hide
17309      */
17310     /**
17311      * @cfg {String} msgFx @hide
17312      */
17313     /**
17314      * @cfg {String} validateOnBlur @hide
17315      */
17316 });
17317
17318 Roo.HtmlEditorCore.white = [
17319         'area', 'br', 'img', 'input', 'hr', 'wbr',
17320         
17321        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
17322        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
17323        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
17324        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
17325        'table',   'ul',         'xmp', 
17326        
17327        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
17328       'thead',   'tr', 
17329      
17330       'dir', 'menu', 'ol', 'ul', 'dl',
17331        
17332       'embed',  'object'
17333 ];
17334
17335
17336 Roo.HtmlEditorCore.black = [
17337     //    'embed',  'object', // enable - backend responsiblity to clean thiese
17338         'applet', // 
17339         'base',   'basefont', 'bgsound', 'blink',  'body', 
17340         'frame',  'frameset', 'head',    'html',   'ilayer', 
17341         'iframe', 'layer',  'link',     'meta',    'object',   
17342         'script', 'style' ,'title',  'xml' // clean later..
17343 ];
17344 Roo.HtmlEditorCore.clean = [
17345     'script', 'style', 'title', 'xml'
17346 ];
17347 Roo.HtmlEditorCore.remove = [
17348     'font'
17349 ];
17350 // attributes..
17351
17352 Roo.HtmlEditorCore.ablack = [
17353     'on'
17354 ];
17355     
17356 Roo.HtmlEditorCore.aclean = [ 
17357     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
17358 ];
17359
17360 // protocols..
17361 Roo.HtmlEditorCore.pwhite= [
17362         'http',  'https',  'mailto'
17363 ];
17364
17365 // white listed style attributes.
17366 Roo.HtmlEditorCore.cwhite= [
17367       //  'text-align', /// default is to allow most things..
17368       
17369          
17370 //        'font-size'//??
17371 ];
17372
17373 // black listed style attributes.
17374 Roo.HtmlEditorCore.cblack= [
17375       //  'font-size' -- this can be set by the project 
17376 ];
17377
17378
17379 Roo.HtmlEditorCore.swapCodes   =[ 
17380     [    8211, "--" ], 
17381     [    8212, "--" ], 
17382     [    8216,  "'" ],  
17383     [    8217, "'" ],  
17384     [    8220, '"' ],  
17385     [    8221, '"' ],  
17386     [    8226, "*" ],  
17387     [    8230, "..." ]
17388 ]; 
17389
17390     /*
17391  * - LGPL
17392  *
17393  * HtmlEditor
17394  * 
17395  */
17396
17397 /**
17398  * @class Roo.bootstrap.HtmlEditor
17399  * @extends Roo.bootstrap.TextArea
17400  * Bootstrap HtmlEditor class
17401
17402  * @constructor
17403  * Create a new HtmlEditor
17404  * @param {Object} config The config object
17405  */
17406
17407 Roo.bootstrap.HtmlEditor = function(config){
17408     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
17409     if (!this.toolbars) {
17410         this.toolbars = [];
17411     }
17412     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
17413     this.addEvents({
17414             /**
17415              * @event initialize
17416              * Fires when the editor is fully initialized (including the iframe)
17417              * @param {HtmlEditor} this
17418              */
17419             initialize: true,
17420             /**
17421              * @event activate
17422              * Fires when the editor is first receives the focus. Any insertion must wait
17423              * until after this event.
17424              * @param {HtmlEditor} this
17425              */
17426             activate: true,
17427              /**
17428              * @event beforesync
17429              * Fires before the textarea is updated with content from the editor iframe. Return false
17430              * to cancel the sync.
17431              * @param {HtmlEditor} this
17432              * @param {String} html
17433              */
17434             beforesync: true,
17435              /**
17436              * @event beforepush
17437              * Fires before the iframe editor is updated with content from the textarea. Return false
17438              * to cancel the push.
17439              * @param {HtmlEditor} this
17440              * @param {String} html
17441              */
17442             beforepush: true,
17443              /**
17444              * @event sync
17445              * Fires when the textarea is updated with content from the editor iframe.
17446              * @param {HtmlEditor} this
17447              * @param {String} html
17448              */
17449             sync: true,
17450              /**
17451              * @event push
17452              * Fires when the iframe editor is updated with content from the textarea.
17453              * @param {HtmlEditor} this
17454              * @param {String} html
17455              */
17456             push: true,
17457              /**
17458              * @event editmodechange
17459              * Fires when the editor switches edit modes
17460              * @param {HtmlEditor} this
17461              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
17462              */
17463             editmodechange: true,
17464             /**
17465              * @event editorevent
17466              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
17467              * @param {HtmlEditor} this
17468              */
17469             editorevent: true,
17470             /**
17471              * @event firstfocus
17472              * Fires when on first focus - needed by toolbars..
17473              * @param {HtmlEditor} this
17474              */
17475             firstfocus: true,
17476             /**
17477              * @event autosave
17478              * Auto save the htmlEditor value as a file into Events
17479              * @param {HtmlEditor} this
17480              */
17481             autosave: true,
17482             /**
17483              * @event savedpreview
17484              * preview the saved version of htmlEditor
17485              * @param {HtmlEditor} this
17486              */
17487             savedpreview: true
17488         });
17489 };
17490
17491
17492 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
17493     
17494     
17495       /**
17496      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
17497      */
17498     toolbars : false,
17499    
17500      /**
17501      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
17502      *                        Roo.resizable.
17503      */
17504     resizable : false,
17505      /**
17506      * @cfg {Number} height (in pixels)
17507      */   
17508     height: 300,
17509    /**
17510      * @cfg {Number} width (in pixels)
17511      */   
17512     width: false,
17513     
17514     /**
17515      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
17516      * 
17517      */
17518     stylesheets: false,
17519     
17520     // id of frame..
17521     frameId: false,
17522     
17523     // private properties
17524     validationEvent : false,
17525     deferHeight: true,
17526     initialized : false,
17527     activated : false,
17528     
17529     onFocus : Roo.emptyFn,
17530     iframePad:3,
17531     hideMode:'offsets',
17532     
17533     
17534     tbContainer : false,
17535     
17536     toolbarContainer :function() {
17537         return this.wrap.select('.x-html-editor-tb',true).first();
17538     },
17539
17540     /**
17541      * Protected method that will not generally be called directly. It
17542      * is called when the editor creates its toolbar. Override this method if you need to
17543      * add custom toolbar buttons.
17544      * @param {HtmlEditor} editor
17545      */
17546     createToolbar : function(){
17547         
17548         Roo.log("create toolbars");
17549         
17550         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
17551         this.toolbars[0].render(this.toolbarContainer());
17552         
17553         return;
17554         
17555 //        if (!editor.toolbars || !editor.toolbars.length) {
17556 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
17557 //        }
17558 //        
17559 //        for (var i =0 ; i < editor.toolbars.length;i++) {
17560 //            editor.toolbars[i] = Roo.factory(
17561 //                    typeof(editor.toolbars[i]) == 'string' ?
17562 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
17563 //                Roo.bootstrap.HtmlEditor);
17564 //            editor.toolbars[i].init(editor);
17565 //        }
17566     },
17567
17568      
17569     // private
17570     onRender : function(ct, position)
17571     {
17572        // Roo.log("Call onRender: " + this.xtype);
17573         var _t = this;
17574         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
17575       
17576         this.wrap = this.inputEl().wrap({
17577             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
17578         });
17579         
17580         this.editorcore.onRender(ct, position);
17581          
17582         if (this.resizable) {
17583             this.resizeEl = new Roo.Resizable(this.wrap, {
17584                 pinned : true,
17585                 wrap: true,
17586                 dynamic : true,
17587                 minHeight : this.height,
17588                 height: this.height,
17589                 handles : this.resizable,
17590                 width: this.width,
17591                 listeners : {
17592                     resize : function(r, w, h) {
17593                         _t.onResize(w,h); // -something
17594                     }
17595                 }
17596             });
17597             
17598         }
17599         this.createToolbar(this);
17600        
17601         
17602         if(!this.width && this.resizable){
17603             this.setSize(this.wrap.getSize());
17604         }
17605         if (this.resizeEl) {
17606             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
17607             // should trigger onReize..
17608         }
17609         
17610     },
17611
17612     // private
17613     onResize : function(w, h)
17614     {
17615         Roo.log('resize: ' +w + ',' + h );
17616         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
17617         var ew = false;
17618         var eh = false;
17619         
17620         if(this.inputEl() ){
17621             if(typeof w == 'number'){
17622                 var aw = w - this.wrap.getFrameWidth('lr');
17623                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
17624                 ew = aw;
17625             }
17626             if(typeof h == 'number'){
17627                  var tbh = -11;  // fixme it needs to tool bar size!
17628                 for (var i =0; i < this.toolbars.length;i++) {
17629                     // fixme - ask toolbars for heights?
17630                     tbh += this.toolbars[i].el.getHeight();
17631                     //if (this.toolbars[i].footer) {
17632                     //    tbh += this.toolbars[i].footer.el.getHeight();
17633                     //}
17634                 }
17635               
17636                 
17637                 
17638                 
17639                 
17640                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
17641                 ah -= 5; // knock a few pixes off for look..
17642                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
17643                 var eh = ah;
17644             }
17645         }
17646         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
17647         this.editorcore.onResize(ew,eh);
17648         
17649     },
17650
17651     /**
17652      * Toggles the editor between standard and source edit mode.
17653      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
17654      */
17655     toggleSourceEdit : function(sourceEditMode)
17656     {
17657         this.editorcore.toggleSourceEdit(sourceEditMode);
17658         
17659         if(this.editorcore.sourceEditMode){
17660             Roo.log('editor - showing textarea');
17661             
17662 //            Roo.log('in');
17663 //            Roo.log(this.syncValue());
17664             this.syncValue();
17665             this.inputEl().removeClass(['hide', 'x-hidden']);
17666             this.inputEl().dom.removeAttribute('tabIndex');
17667             this.inputEl().focus();
17668         }else{
17669             Roo.log('editor - hiding textarea');
17670 //            Roo.log('out')
17671 //            Roo.log(this.pushValue()); 
17672             this.pushValue();
17673             
17674             this.inputEl().addClass(['hide', 'x-hidden']);
17675             this.inputEl().dom.setAttribute('tabIndex', -1);
17676             //this.deferFocus();
17677         }
17678          
17679         if(this.resizable){
17680             this.setSize(this.wrap.getSize());
17681         }
17682         
17683         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
17684     },
17685  
17686     // private (for BoxComponent)
17687     adjustSize : Roo.BoxComponent.prototype.adjustSize,
17688
17689     // private (for BoxComponent)
17690     getResizeEl : function(){
17691         return this.wrap;
17692     },
17693
17694     // private (for BoxComponent)
17695     getPositionEl : function(){
17696         return this.wrap;
17697     },
17698
17699     // private
17700     initEvents : function(){
17701         this.originalValue = this.getValue();
17702     },
17703
17704 //    /**
17705 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
17706 //     * @method
17707 //     */
17708 //    markInvalid : Roo.emptyFn,
17709 //    /**
17710 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
17711 //     * @method
17712 //     */
17713 //    clearInvalid : Roo.emptyFn,
17714
17715     setValue : function(v){
17716         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
17717         this.editorcore.pushValue();
17718     },
17719
17720      
17721     // private
17722     deferFocus : function(){
17723         this.focus.defer(10, this);
17724     },
17725
17726     // doc'ed in Field
17727     focus : function(){
17728         this.editorcore.focus();
17729         
17730     },
17731       
17732
17733     // private
17734     onDestroy : function(){
17735         
17736         
17737         
17738         if(this.rendered){
17739             
17740             for (var i =0; i < this.toolbars.length;i++) {
17741                 // fixme - ask toolbars for heights?
17742                 this.toolbars[i].onDestroy();
17743             }
17744             
17745             this.wrap.dom.innerHTML = '';
17746             this.wrap.remove();
17747         }
17748     },
17749
17750     // private
17751     onFirstFocus : function(){
17752         //Roo.log("onFirstFocus");
17753         this.editorcore.onFirstFocus();
17754          for (var i =0; i < this.toolbars.length;i++) {
17755             this.toolbars[i].onFirstFocus();
17756         }
17757         
17758     },
17759     
17760     // private
17761     syncValue : function()
17762     {   
17763         this.editorcore.syncValue();
17764     },
17765     
17766     pushValue : function()
17767     {   
17768         this.editorcore.pushValue();
17769     }
17770      
17771     
17772     // hide stuff that is not compatible
17773     /**
17774      * @event blur
17775      * @hide
17776      */
17777     /**
17778      * @event change
17779      * @hide
17780      */
17781     /**
17782      * @event focus
17783      * @hide
17784      */
17785     /**
17786      * @event specialkey
17787      * @hide
17788      */
17789     /**
17790      * @cfg {String} fieldClass @hide
17791      */
17792     /**
17793      * @cfg {String} focusClass @hide
17794      */
17795     /**
17796      * @cfg {String} autoCreate @hide
17797      */
17798     /**
17799      * @cfg {String} inputType @hide
17800      */
17801     /**
17802      * @cfg {String} invalidClass @hide
17803      */
17804     /**
17805      * @cfg {String} invalidText @hide
17806      */
17807     /**
17808      * @cfg {String} msgFx @hide
17809      */
17810     /**
17811      * @cfg {String} validateOnBlur @hide
17812      */
17813 });
17814  
17815     
17816    
17817    
17818    
17819       
17820 Roo.namespace('Roo.bootstrap.htmleditor');
17821 /**
17822  * @class Roo.bootstrap.HtmlEditorToolbar1
17823  * Basic Toolbar
17824  * 
17825  * Usage:
17826  *
17827  new Roo.bootstrap.HtmlEditor({
17828     ....
17829     toolbars : [
17830         new Roo.bootstrap.HtmlEditorToolbar1({
17831             disable : { fonts: 1 , format: 1, ..., ... , ...],
17832             btns : [ .... ]
17833         })
17834     }
17835      
17836  * 
17837  * @cfg {Object} disable List of elements to disable..
17838  * @cfg {Array} btns List of additional buttons.
17839  * 
17840  * 
17841  * NEEDS Extra CSS? 
17842  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
17843  */
17844  
17845 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
17846 {
17847     
17848     Roo.apply(this, config);
17849     
17850     // default disabled, based on 'good practice'..
17851     this.disable = this.disable || {};
17852     Roo.applyIf(this.disable, {
17853         fontSize : true,
17854         colors : true,
17855         specialElements : true
17856     });
17857     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
17858     
17859     this.editor = config.editor;
17860     this.editorcore = config.editor.editorcore;
17861     
17862     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
17863     
17864     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
17865     // dont call parent... till later.
17866 }
17867 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
17868      
17869     bar : true,
17870     
17871     editor : false,
17872     editorcore : false,
17873     
17874     
17875     formats : [
17876         "p" ,  
17877         "h1","h2","h3","h4","h5","h6", 
17878         "pre", "code", 
17879         "abbr", "acronym", "address", "cite", "samp", "var",
17880         'div','span'
17881     ],
17882     
17883     onRender : function(ct, position)
17884     {
17885        // Roo.log("Call onRender: " + this.xtype);
17886         
17887        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
17888        Roo.log(this.el);
17889        this.el.dom.style.marginBottom = '0';
17890        var _this = this;
17891        var editorcore = this.editorcore;
17892        var editor= this.editor;
17893        
17894        var children = [];
17895        var btn = function(id,cmd , toggle, handler){
17896        
17897             var  event = toggle ? 'toggle' : 'click';
17898        
17899             var a = {
17900                 size : 'sm',
17901                 xtype: 'Button',
17902                 xns: Roo.bootstrap,
17903                 glyphicon : id,
17904                 cmd : id || cmd,
17905                 enableToggle:toggle !== false,
17906                 //html : 'submit'
17907                 pressed : toggle ? false : null,
17908                 listeners : {}
17909             }
17910             a.listeners[toggle ? 'toggle' : 'click'] = function() {
17911                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
17912             }
17913             children.push(a);
17914             return a;
17915        }
17916         
17917         var style = {
17918                 xtype: 'Button',
17919                 size : 'sm',
17920                 xns: Roo.bootstrap,
17921                 glyphicon : 'font',
17922                 //html : 'submit'
17923                 menu : {
17924                     xtype: 'Menu',
17925                     xns: Roo.bootstrap,
17926                     items:  []
17927                 }
17928         };
17929         Roo.each(this.formats, function(f) {
17930             style.menu.items.push({
17931                 xtype :'MenuItem',
17932                 xns: Roo.bootstrap,
17933                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
17934                 tagname : f,
17935                 listeners : {
17936                     click : function()
17937                     {
17938                         editorcore.insertTag(this.tagname);
17939                         editor.focus();
17940                     }
17941                 }
17942                 
17943             });
17944         });
17945          children.push(style);   
17946             
17947             
17948         btn('bold',false,true);
17949         btn('italic',false,true);
17950         btn('align-left', 'justifyleft',true);
17951         btn('align-center', 'justifycenter',true);
17952         btn('align-right' , 'justifyright',true);
17953         btn('link', false, false, function(btn) {
17954             //Roo.log("create link?");
17955             var url = prompt(this.createLinkText, this.defaultLinkValue);
17956             if(url && url != 'http:/'+'/'){
17957                 this.editorcore.relayCmd('createlink', url);
17958             }
17959         }),
17960         btn('list','insertunorderedlist',true);
17961         btn('pencil', false,true, function(btn){
17962                 Roo.log(this);
17963                 
17964                 this.toggleSourceEdit(btn.pressed);
17965         });
17966         /*
17967         var cog = {
17968                 xtype: 'Button',
17969                 size : 'sm',
17970                 xns: Roo.bootstrap,
17971                 glyphicon : 'cog',
17972                 //html : 'submit'
17973                 menu : {
17974                     xtype: 'Menu',
17975                     xns: Roo.bootstrap,
17976                     items:  []
17977                 }
17978         };
17979         
17980         cog.menu.items.push({
17981             xtype :'MenuItem',
17982             xns: Roo.bootstrap,
17983             html : Clean styles,
17984             tagname : f,
17985             listeners : {
17986                 click : function()
17987                 {
17988                     editorcore.insertTag(this.tagname);
17989                     editor.focus();
17990                 }
17991             }
17992             
17993         });
17994        */
17995         
17996          
17997        this.xtype = 'NavSimplebar';
17998         
17999         for(var i=0;i< children.length;i++) {
18000             
18001             this.buttons.add(this.addxtypeChild(children[i]));
18002             
18003         }
18004         
18005         editor.on('editorevent', this.updateToolbar, this);
18006     },
18007     onBtnClick : function(id)
18008     {
18009        this.editorcore.relayCmd(id);
18010        this.editorcore.focus();
18011     },
18012     
18013     /**
18014      * Protected method that will not generally be called directly. It triggers
18015      * a toolbar update by reading the markup state of the current selection in the editor.
18016      */
18017     updateToolbar: function(){
18018
18019         if(!this.editorcore.activated){
18020             this.editor.onFirstFocus(); // is this neeed?
18021             return;
18022         }
18023
18024         var btns = this.buttons; 
18025         var doc = this.editorcore.doc;
18026         btns.get('bold').setActive(doc.queryCommandState('bold'));
18027         btns.get('italic').setActive(doc.queryCommandState('italic'));
18028         //btns.get('underline').setActive(doc.queryCommandState('underline'));
18029         
18030         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
18031         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
18032         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
18033         
18034         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
18035         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
18036          /*
18037         
18038         var ans = this.editorcore.getAllAncestors();
18039         if (this.formatCombo) {
18040             
18041             
18042             var store = this.formatCombo.store;
18043             this.formatCombo.setValue("");
18044             for (var i =0; i < ans.length;i++) {
18045                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
18046                     // select it..
18047                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
18048                     break;
18049                 }
18050             }
18051         }
18052         
18053         
18054         
18055         // hides menus... - so this cant be on a menu...
18056         Roo.bootstrap.MenuMgr.hideAll();
18057         */
18058         Roo.bootstrap.MenuMgr.hideAll();
18059         //this.editorsyncValue();
18060     },
18061     onFirstFocus: function() {
18062         this.buttons.each(function(item){
18063            item.enable();
18064         });
18065     },
18066     toggleSourceEdit : function(sourceEditMode){
18067         
18068           
18069         if(sourceEditMode){
18070             Roo.log("disabling buttons");
18071            this.buttons.each( function(item){
18072                 if(item.cmd != 'pencil'){
18073                     item.disable();
18074                 }
18075             });
18076           
18077         }else{
18078             Roo.log("enabling buttons");
18079             if(this.editorcore.initialized){
18080                 this.buttons.each( function(item){
18081                     item.enable();
18082                 });
18083             }
18084             
18085         }
18086         Roo.log("calling toggole on editor");
18087         // tell the editor that it's been pressed..
18088         this.editor.toggleSourceEdit(sourceEditMode);
18089        
18090     }
18091 });
18092
18093
18094
18095
18096
18097 /**
18098  * @class Roo.bootstrap.Table.AbstractSelectionModel
18099  * @extends Roo.util.Observable
18100  * Abstract base class for grid SelectionModels.  It provides the interface that should be
18101  * implemented by descendant classes.  This class should not be directly instantiated.
18102  * @constructor
18103  */
18104 Roo.bootstrap.Table.AbstractSelectionModel = function(){
18105     this.locked = false;
18106     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
18107 };
18108
18109
18110 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
18111     /** @ignore Called by the grid automatically. Do not call directly. */
18112     init : function(grid){
18113         this.grid = grid;
18114         this.initEvents();
18115     },
18116
18117     /**
18118      * Locks the selections.
18119      */
18120     lock : function(){
18121         this.locked = true;
18122     },
18123
18124     /**
18125      * Unlocks the selections.
18126      */
18127     unlock : function(){
18128         this.locked = false;
18129     },
18130
18131     /**
18132      * Returns true if the selections are locked.
18133      * @return {Boolean}
18134      */
18135     isLocked : function(){
18136         return this.locked;
18137     }
18138 });
18139 /**
18140  * @extends Roo.bootstrap.Table.AbstractSelectionModel
18141  * @class Roo.bootstrap.Table.RowSelectionModel
18142  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
18143  * It supports multiple selections and keyboard selection/navigation. 
18144  * @constructor
18145  * @param {Object} config
18146  */
18147
18148 Roo.bootstrap.Table.RowSelectionModel = function(config){
18149     Roo.apply(this, config);
18150     this.selections = new Roo.util.MixedCollection(false, function(o){
18151         return o.id;
18152     });
18153
18154     this.last = false;
18155     this.lastActive = false;
18156
18157     this.addEvents({
18158         /**
18159              * @event selectionchange
18160              * Fires when the selection changes
18161              * @param {SelectionModel} this
18162              */
18163             "selectionchange" : true,
18164         /**
18165              * @event afterselectionchange
18166              * Fires after the selection changes (eg. by key press or clicking)
18167              * @param {SelectionModel} this
18168              */
18169             "afterselectionchange" : true,
18170         /**
18171              * @event beforerowselect
18172              * Fires when a row is selected being selected, return false to cancel.
18173              * @param {SelectionModel} this
18174              * @param {Number} rowIndex The selected index
18175              * @param {Boolean} keepExisting False if other selections will be cleared
18176              */
18177             "beforerowselect" : true,
18178         /**
18179              * @event rowselect
18180              * Fires when a row is selected.
18181              * @param {SelectionModel} this
18182              * @param {Number} rowIndex The selected index
18183              * @param {Roo.data.Record} r The record
18184              */
18185             "rowselect" : true,
18186         /**
18187              * @event rowdeselect
18188              * Fires when a row is deselected.
18189              * @param {SelectionModel} this
18190              * @param {Number} rowIndex The selected index
18191              */
18192         "rowdeselect" : true
18193     });
18194     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
18195     this.locked = false;
18196 };
18197
18198 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
18199     /**
18200      * @cfg {Boolean} singleSelect
18201      * True to allow selection of only one row at a time (defaults to false)
18202      */
18203     singleSelect : false,
18204
18205     // private
18206     initEvents : function(){
18207
18208         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
18209             this.grid.on("mousedown", this.handleMouseDown, this);
18210         }else{ // allow click to work like normal
18211             this.grid.on("rowclick", this.handleDragableRowClick, this);
18212         }
18213
18214         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
18215             "up" : function(e){
18216                 if(!e.shiftKey){
18217                     this.selectPrevious(e.shiftKey);
18218                 }else if(this.last !== false && this.lastActive !== false){
18219                     var last = this.last;
18220                     this.selectRange(this.last,  this.lastActive-1);
18221                     this.grid.getView().focusRow(this.lastActive);
18222                     if(last !== false){
18223                         this.last = last;
18224                     }
18225                 }else{
18226                     this.selectFirstRow();
18227                 }
18228                 this.fireEvent("afterselectionchange", this);
18229             },
18230             "down" : function(e){
18231                 if(!e.shiftKey){
18232                     this.selectNext(e.shiftKey);
18233                 }else if(this.last !== false && this.lastActive !== false){
18234                     var last = this.last;
18235                     this.selectRange(this.last,  this.lastActive+1);
18236                     this.grid.getView().focusRow(this.lastActive);
18237                     if(last !== false){
18238                         this.last = last;
18239                     }
18240                 }else{
18241                     this.selectFirstRow();
18242                 }
18243                 this.fireEvent("afterselectionchange", this);
18244             },
18245             scope: this
18246         });
18247
18248         var view = this.grid.view;
18249         view.on("refresh", this.onRefresh, this);
18250         view.on("rowupdated", this.onRowUpdated, this);
18251         view.on("rowremoved", this.onRemove, this);
18252     },
18253
18254     // private
18255     onRefresh : function(){
18256         var ds = this.grid.dataSource, i, v = this.grid.view;
18257         var s = this.selections;
18258         s.each(function(r){
18259             if((i = ds.indexOfId(r.id)) != -1){
18260                 v.onRowSelect(i);
18261             }else{
18262                 s.remove(r);
18263             }
18264         });
18265     },
18266
18267     // private
18268     onRemove : function(v, index, r){
18269         this.selections.remove(r);
18270     },
18271
18272     // private
18273     onRowUpdated : function(v, index, r){
18274         if(this.isSelected(r)){
18275             v.onRowSelect(index);
18276         }
18277     },
18278
18279     /**
18280      * Select records.
18281      * @param {Array} records The records to select
18282      * @param {Boolean} keepExisting (optional) True to keep existing selections
18283      */
18284     selectRecords : function(records, keepExisting){
18285         if(!keepExisting){
18286             this.clearSelections();
18287         }
18288         var ds = this.grid.dataSource;
18289         for(var i = 0, len = records.length; i < len; i++){
18290             this.selectRow(ds.indexOf(records[i]), true);
18291         }
18292     },
18293
18294     /**
18295      * Gets the number of selected rows.
18296      * @return {Number}
18297      */
18298     getCount : function(){
18299         return this.selections.length;
18300     },
18301
18302     /**
18303      * Selects the first row in the grid.
18304      */
18305     selectFirstRow : function(){
18306         this.selectRow(0);
18307     },
18308
18309     /**
18310      * Select the last row.
18311      * @param {Boolean} keepExisting (optional) True to keep existing selections
18312      */
18313     selectLastRow : function(keepExisting){
18314         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
18315     },
18316
18317     /**
18318      * Selects the row immediately following the last selected row.
18319      * @param {Boolean} keepExisting (optional) True to keep existing selections
18320      */
18321     selectNext : function(keepExisting){
18322         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
18323             this.selectRow(this.last+1, keepExisting);
18324             this.grid.getView().focusRow(this.last);
18325         }
18326     },
18327
18328     /**
18329      * Selects the row that precedes the last selected row.
18330      * @param {Boolean} keepExisting (optional) True to keep existing selections
18331      */
18332     selectPrevious : function(keepExisting){
18333         if(this.last){
18334             this.selectRow(this.last-1, keepExisting);
18335             this.grid.getView().focusRow(this.last);
18336         }
18337     },
18338
18339     /**
18340      * Returns the selected records
18341      * @return {Array} Array of selected records
18342      */
18343     getSelections : function(){
18344         return [].concat(this.selections.items);
18345     },
18346
18347     /**
18348      * Returns the first selected record.
18349      * @return {Record}
18350      */
18351     getSelected : function(){
18352         return this.selections.itemAt(0);
18353     },
18354
18355
18356     /**
18357      * Clears all selections.
18358      */
18359     clearSelections : function(fast){
18360         if(this.locked) return;
18361         if(fast !== true){
18362             var ds = this.grid.dataSource;
18363             var s = this.selections;
18364             s.each(function(r){
18365                 this.deselectRow(ds.indexOfId(r.id));
18366             }, this);
18367             s.clear();
18368         }else{
18369             this.selections.clear();
18370         }
18371         this.last = false;
18372     },
18373
18374
18375     /**
18376      * Selects all rows.
18377      */
18378     selectAll : function(){
18379         if(this.locked) return;
18380         this.selections.clear();
18381         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
18382             this.selectRow(i, true);
18383         }
18384     },
18385
18386     /**
18387      * Returns True if there is a selection.
18388      * @return {Boolean}
18389      */
18390     hasSelection : function(){
18391         return this.selections.length > 0;
18392     },
18393
18394     /**
18395      * Returns True if the specified row is selected.
18396      * @param {Number/Record} record The record or index of the record to check
18397      * @return {Boolean}
18398      */
18399     isSelected : function(index){
18400         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
18401         return (r && this.selections.key(r.id) ? true : false);
18402     },
18403
18404     /**
18405      * Returns True if the specified record id is selected.
18406      * @param {String} id The id of record to check
18407      * @return {Boolean}
18408      */
18409     isIdSelected : function(id){
18410         return (this.selections.key(id) ? true : false);
18411     },
18412
18413     // private
18414     handleMouseDown : function(e, t){
18415         var view = this.grid.getView(), rowIndex;
18416         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
18417             return;
18418         };
18419         if(e.shiftKey && this.last !== false){
18420             var last = this.last;
18421             this.selectRange(last, rowIndex, e.ctrlKey);
18422             this.last = last; // reset the last
18423             view.focusRow(rowIndex);
18424         }else{
18425             var isSelected = this.isSelected(rowIndex);
18426             if(e.button !== 0 && isSelected){
18427                 view.focusRow(rowIndex);
18428             }else if(e.ctrlKey && isSelected){
18429                 this.deselectRow(rowIndex);
18430             }else if(!isSelected){
18431                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
18432                 view.focusRow(rowIndex);
18433             }
18434         }
18435         this.fireEvent("afterselectionchange", this);
18436     },
18437     // private
18438     handleDragableRowClick :  function(grid, rowIndex, e) 
18439     {
18440         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
18441             this.selectRow(rowIndex, false);
18442             grid.view.focusRow(rowIndex);
18443              this.fireEvent("afterselectionchange", this);
18444         }
18445     },
18446     
18447     /**
18448      * Selects multiple rows.
18449      * @param {Array} rows Array of the indexes of the row to select
18450      * @param {Boolean} keepExisting (optional) True to keep existing selections
18451      */
18452     selectRows : function(rows, keepExisting){
18453         if(!keepExisting){
18454             this.clearSelections();
18455         }
18456         for(var i = 0, len = rows.length; i < len; i++){
18457             this.selectRow(rows[i], true);
18458         }
18459     },
18460
18461     /**
18462      * Selects a range of rows. All rows in between startRow and endRow are also selected.
18463      * @param {Number} startRow The index of the first row in the range
18464      * @param {Number} endRow The index of the last row in the range
18465      * @param {Boolean} keepExisting (optional) True to retain existing selections
18466      */
18467     selectRange : function(startRow, endRow, keepExisting){
18468         if(this.locked) return;
18469         if(!keepExisting){
18470             this.clearSelections();
18471         }
18472         if(startRow <= endRow){
18473             for(var i = startRow; i <= endRow; i++){
18474                 this.selectRow(i, true);
18475             }
18476         }else{
18477             for(var i = startRow; i >= endRow; i--){
18478                 this.selectRow(i, true);
18479             }
18480         }
18481     },
18482
18483     /**
18484      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
18485      * @param {Number} startRow The index of the first row in the range
18486      * @param {Number} endRow The index of the last row in the range
18487      */
18488     deselectRange : function(startRow, endRow, preventViewNotify){
18489         if(this.locked) return;
18490         for(var i = startRow; i <= endRow; i++){
18491             this.deselectRow(i, preventViewNotify);
18492         }
18493     },
18494
18495     /**
18496      * Selects a row.
18497      * @param {Number} row The index of the row to select
18498      * @param {Boolean} keepExisting (optional) True to keep existing selections
18499      */
18500     selectRow : function(index, keepExisting, preventViewNotify){
18501         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
18502         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
18503             if(!keepExisting || this.singleSelect){
18504                 this.clearSelections();
18505             }
18506             var r = this.grid.dataSource.getAt(index);
18507             this.selections.add(r);
18508             this.last = this.lastActive = index;
18509             if(!preventViewNotify){
18510                 this.grid.getView().onRowSelect(index);
18511             }
18512             this.fireEvent("rowselect", this, index, r);
18513             this.fireEvent("selectionchange", this);
18514         }
18515     },
18516
18517     /**
18518      * Deselects a row.
18519      * @param {Number} row The index of the row to deselect
18520      */
18521     deselectRow : function(index, preventViewNotify){
18522         if(this.locked) return;
18523         if(this.last == index){
18524             this.last = false;
18525         }
18526         if(this.lastActive == index){
18527             this.lastActive = false;
18528         }
18529         var r = this.grid.dataSource.getAt(index);
18530         this.selections.remove(r);
18531         if(!preventViewNotify){
18532             this.grid.getView().onRowDeselect(index);
18533         }
18534         this.fireEvent("rowdeselect", this, index);
18535         this.fireEvent("selectionchange", this);
18536     },
18537
18538     // private
18539     restoreLast : function(){
18540         if(this._last){
18541             this.last = this._last;
18542         }
18543     },
18544
18545     // private
18546     acceptsNav : function(row, col, cm){
18547         return !cm.isHidden(col) && cm.isCellEditable(col, row);
18548     },
18549
18550     // private
18551     onEditorKey : function(field, e){
18552         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
18553         if(k == e.TAB){
18554             e.stopEvent();
18555             ed.completeEdit();
18556             if(e.shiftKey){
18557                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
18558             }else{
18559                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
18560             }
18561         }else if(k == e.ENTER && !e.ctrlKey){
18562             e.stopEvent();
18563             ed.completeEdit();
18564             if(e.shiftKey){
18565                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
18566             }else{
18567                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
18568             }
18569         }else if(k == e.ESC){
18570             ed.cancelEdit();
18571         }
18572         if(newCell){
18573             g.startEditing(newCell[0], newCell[1]);
18574         }
18575     }
18576 });/*
18577  * Based on:
18578  * Ext JS Library 1.1.1
18579  * Copyright(c) 2006-2007, Ext JS, LLC.
18580  *
18581  * Originally Released Under LGPL - original licence link has changed is not relivant.
18582  *
18583  * Fork - LGPL
18584  * <script type="text/javascript">
18585  */
18586  
18587 /**
18588  * @class Roo.bootstrap.PagingToolbar
18589  * @extends Roo.Row
18590  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
18591  * @constructor
18592  * Create a new PagingToolbar
18593  * @param {Object} config The config object
18594  */
18595 Roo.bootstrap.PagingToolbar = function(config)
18596 {
18597     // old args format still supported... - xtype is prefered..
18598         // created from xtype...
18599     var ds = config.dataSource;
18600     this.toolbarItems = [];
18601     if (config.items) {
18602         this.toolbarItems = config.items;
18603 //        config.items = [];
18604     }
18605     
18606     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
18607     this.ds = ds;
18608     this.cursor = 0;
18609     if (ds) { 
18610         this.bind(ds);
18611     }
18612     
18613     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
18614     
18615 };
18616
18617 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
18618     /**
18619      * @cfg {Roo.data.Store} dataSource
18620      * The underlying data store providing the paged data
18621      */
18622     /**
18623      * @cfg {String/HTMLElement/Element} container
18624      * container The id or element that will contain the toolbar
18625      */
18626     /**
18627      * @cfg {Boolean} displayInfo
18628      * True to display the displayMsg (defaults to false)
18629      */
18630     /**
18631      * @cfg {Number} pageSize
18632      * The number of records to display per page (defaults to 20)
18633      */
18634     pageSize: 20,
18635     /**
18636      * @cfg {String} displayMsg
18637      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
18638      */
18639     displayMsg : 'Displaying {0} - {1} of {2}',
18640     /**
18641      * @cfg {String} emptyMsg
18642      * The message to display when no records are found (defaults to "No data to display")
18643      */
18644     emptyMsg : 'No data to display',
18645     /**
18646      * Customizable piece of the default paging text (defaults to "Page")
18647      * @type String
18648      */
18649     beforePageText : "Page",
18650     /**
18651      * Customizable piece of the default paging text (defaults to "of %0")
18652      * @type String
18653      */
18654     afterPageText : "of {0}",
18655     /**
18656      * Customizable piece of the default paging text (defaults to "First Page")
18657      * @type String
18658      */
18659     firstText : "First Page",
18660     /**
18661      * Customizable piece of the default paging text (defaults to "Previous Page")
18662      * @type String
18663      */
18664     prevText : "Previous Page",
18665     /**
18666      * Customizable piece of the default paging text (defaults to "Next Page")
18667      * @type String
18668      */
18669     nextText : "Next Page",
18670     /**
18671      * Customizable piece of the default paging text (defaults to "Last Page")
18672      * @type String
18673      */
18674     lastText : "Last Page",
18675     /**
18676      * Customizable piece of the default paging text (defaults to "Refresh")
18677      * @type String
18678      */
18679     refreshText : "Refresh",
18680
18681     buttons : false,
18682     // private
18683     onRender : function(ct, position) 
18684     {
18685         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
18686         this.navgroup.parentId = this.id;
18687         this.navgroup.onRender(this.el, null);
18688         // add the buttons to the navgroup
18689         
18690         if(this.displayInfo){
18691             Roo.log(this.el.select('ul.navbar-nav',true).first());
18692             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
18693             this.displayEl = this.el.select('.x-paging-info', true).first();
18694 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
18695 //            this.displayEl = navel.el.select('span',true).first();
18696         }
18697         
18698         var _this = this;
18699         
18700         if(this.buttons){
18701             Roo.each(_this.buttons, function(e){
18702                Roo.factory(e).onRender(_this.el, null);
18703             });
18704         }
18705             
18706         Roo.each(_this.toolbarItems, function(e) {
18707             _this.navgroup.addItem(e);
18708         });
18709         
18710         this.first = this.navgroup.addItem({
18711             tooltip: this.firstText,
18712             cls: "prev",
18713             icon : 'fa fa-backward',
18714             disabled: true,
18715             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
18716         });
18717         
18718         this.prev =  this.navgroup.addItem({
18719             tooltip: this.prevText,
18720             cls: "prev",
18721             icon : 'fa fa-step-backward',
18722             disabled: true,
18723             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
18724         });
18725     //this.addSeparator();
18726         
18727         
18728         var field = this.navgroup.addItem( {
18729             tagtype : 'span',
18730             cls : 'x-paging-position',
18731             
18732             html : this.beforePageText  +
18733                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
18734                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
18735          } ); //?? escaped?
18736         
18737         this.field = field.el.select('input', true).first();
18738         this.field.on("keydown", this.onPagingKeydown, this);
18739         this.field.on("focus", function(){this.dom.select();});
18740     
18741     
18742         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
18743         //this.field.setHeight(18);
18744         //this.addSeparator();
18745         this.next = this.navgroup.addItem({
18746             tooltip: this.nextText,
18747             cls: "next",
18748             html : ' <i class="fa fa-step-forward">',
18749             disabled: true,
18750             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
18751         });
18752         this.last = this.navgroup.addItem({
18753             tooltip: this.lastText,
18754             icon : 'fa fa-forward',
18755             cls: "next",
18756             disabled: true,
18757             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
18758         });
18759     //this.addSeparator();
18760         this.loading = this.navgroup.addItem({
18761             tooltip: this.refreshText,
18762             icon: 'fa fa-refresh',
18763             
18764             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
18765         });
18766
18767     },
18768
18769     // private
18770     updateInfo : function(){
18771         if(this.displayEl){
18772             var count = this.ds.getCount();
18773             var msg = count == 0 ?
18774                 this.emptyMsg :
18775                 String.format(
18776                     this.displayMsg,
18777                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
18778                 );
18779             this.displayEl.update(msg);
18780         }
18781     },
18782
18783     // private
18784     onLoad : function(ds, r, o){
18785        this.cursor = o.params ? o.params.start : 0;
18786        var d = this.getPageData(),
18787             ap = d.activePage,
18788             ps = d.pages;
18789         
18790        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
18791        this.field.dom.value = ap;
18792        this.first.setDisabled(ap == 1);
18793        this.prev.setDisabled(ap == 1);
18794        this.next.setDisabled(ap == ps);
18795        this.last.setDisabled(ap == ps);
18796        this.loading.enable();
18797        this.updateInfo();
18798     },
18799
18800     // private
18801     getPageData : function(){
18802         var total = this.ds.getTotalCount();
18803         return {
18804             total : total,
18805             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
18806             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
18807         };
18808     },
18809
18810     // private
18811     onLoadError : function(){
18812         this.loading.enable();
18813     },
18814
18815     // private
18816     onPagingKeydown : function(e){
18817         var k = e.getKey();
18818         var d = this.getPageData();
18819         if(k == e.RETURN){
18820             var v = this.field.dom.value, pageNum;
18821             if(!v || isNaN(pageNum = parseInt(v, 10))){
18822                 this.field.dom.value = d.activePage;
18823                 return;
18824             }
18825             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
18826             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
18827             e.stopEvent();
18828         }
18829         else if(k == e.HOME || (k == e.UP && e.ctrlKey) || (k == e.PAGEUP && e.ctrlKey) || (k == e.RIGHT && e.ctrlKey) || k == e.END || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey))
18830         {
18831           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
18832           this.field.dom.value = pageNum;
18833           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
18834           e.stopEvent();
18835         }
18836         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
18837         {
18838           var v = this.field.dom.value, pageNum; 
18839           var increment = (e.shiftKey) ? 10 : 1;
18840           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
18841             increment *= -1;
18842           if(!v || isNaN(pageNum = parseInt(v, 10))) {
18843             this.field.dom.value = d.activePage;
18844             return;
18845           }
18846           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
18847           {
18848             this.field.dom.value = parseInt(v, 10) + increment;
18849             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
18850             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
18851           }
18852           e.stopEvent();
18853         }
18854     },
18855
18856     // private
18857     beforeLoad : function(){
18858         if(this.loading){
18859             this.loading.disable();
18860         }
18861     },
18862
18863     // private
18864     onClick : function(which){
18865         var ds = this.ds;
18866         if (!ds) {
18867             return;
18868         }
18869         switch(which){
18870             case "first":
18871                 ds.load({params:{start: 0, limit: this.pageSize}});
18872             break;
18873             case "prev":
18874                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
18875             break;
18876             case "next":
18877                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
18878             break;
18879             case "last":
18880                 var total = ds.getTotalCount();
18881                 var extra = total % this.pageSize;
18882                 var lastStart = extra ? (total - extra) : total-this.pageSize;
18883                 ds.load({params:{start: lastStart, limit: this.pageSize}});
18884             break;
18885             case "refresh":
18886                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
18887             break;
18888         }
18889     },
18890
18891     /**
18892      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
18893      * @param {Roo.data.Store} store The data store to unbind
18894      */
18895     unbind : function(ds){
18896         ds.un("beforeload", this.beforeLoad, this);
18897         ds.un("load", this.onLoad, this);
18898         ds.un("loadexception", this.onLoadError, this);
18899         ds.un("remove", this.updateInfo, this);
18900         ds.un("add", this.updateInfo, this);
18901         this.ds = undefined;
18902     },
18903
18904     /**
18905      * Binds the paging toolbar to the specified {@link Roo.data.Store}
18906      * @param {Roo.data.Store} store The data store to bind
18907      */
18908     bind : function(ds){
18909         ds.on("beforeload", this.beforeLoad, this);
18910         ds.on("load", this.onLoad, this);
18911         ds.on("loadexception", this.onLoadError, this);
18912         ds.on("remove", this.updateInfo, this);
18913         ds.on("add", this.updateInfo, this);
18914         this.ds = ds;
18915     }
18916 });/*
18917  * - LGPL
18918  *
18919  * element
18920  * 
18921  */
18922
18923 /**
18924  * @class Roo.bootstrap.MessageBar
18925  * @extends Roo.bootstrap.Component
18926  * Bootstrap MessageBar class
18927  * @cfg {String} html contents of the MessageBar
18928  * @cfg {String} weight (info | success | warning | danger) default info
18929  * @cfg {String} beforeClass insert the bar before the given class
18930  * @cfg {Boolean} closable (true | false) default false
18931  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
18932  * 
18933  * @constructor
18934  * Create a new Element
18935  * @param {Object} config The config object
18936  */
18937
18938 Roo.bootstrap.MessageBar = function(config){
18939     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
18940 };
18941
18942 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
18943     
18944     html: '',
18945     weight: 'info',
18946     closable: false,
18947     fixed: false,
18948     beforeClass: 'bootstrap-sticky-wrap',
18949     
18950     getAutoCreate : function(){
18951         
18952         var cfg = {
18953             tag: 'div',
18954             cls: 'alert alert-dismissable alert-' + this.weight,
18955             cn: [
18956                 {
18957                     tag: 'span',
18958                     cls: 'message',
18959                     html: this.html || ''
18960                 }
18961             ]
18962         }
18963         
18964         if(this.fixed){
18965             cfg.cls += ' alert-messages-fixed';
18966         }
18967         
18968         if(this.closable){
18969             cfg.cn.push({
18970                 tag: 'button',
18971                 cls: 'close',
18972                 html: 'x'
18973             });
18974         }
18975         
18976         return cfg;
18977     },
18978     
18979     onRender : function(ct, position)
18980     {
18981         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
18982         
18983         if(!this.el){
18984             var cfg = Roo.apply({},  this.getAutoCreate());
18985             cfg.id = Roo.id();
18986             
18987             if (this.cls) {
18988                 cfg.cls += ' ' + this.cls;
18989             }
18990             if (this.style) {
18991                 cfg.style = this.style;
18992             }
18993             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
18994             
18995             this.el.setVisibilityMode(Roo.Element.DISPLAY);
18996         }
18997         
18998         this.el.select('>button.close').on('click', this.hide, this);
18999         
19000     },
19001     
19002     show : function()
19003     {
19004         if (!this.rendered) {
19005             this.render();
19006         }
19007         
19008         this.el.show();
19009         
19010         this.fireEvent('show', this);
19011         
19012     },
19013     
19014     hide : function()
19015     {
19016         if (!this.rendered) {
19017             this.render();
19018         }
19019         
19020         this.el.hide();
19021         
19022         this.fireEvent('hide', this);
19023     },
19024     
19025     update : function()
19026     {
19027 //        var e = this.el.dom.firstChild;
19028 //        
19029 //        if(this.closable){
19030 //            e = e.nextSibling;
19031 //        }
19032 //        
19033 //        e.data = this.html || '';
19034
19035         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
19036     }
19037    
19038 });
19039
19040  
19041
19042      /*
19043  * - LGPL
19044  *
19045  * Graph
19046  * 
19047  */
19048
19049
19050 /**
19051  * @class Roo.bootstrap.Graph
19052  * @extends Roo.bootstrap.Component
19053  * Bootstrap Graph class
19054 > Prameters
19055  -sm {number} sm 4
19056  -md {number} md 5
19057  @cfg {String} graphtype  bar | vbar | pie
19058  @cfg {number} g_x coodinator | centre x (pie)
19059  @cfg {number} g_y coodinator | centre y (pie)
19060  @cfg {number} g_r radius (pie)
19061  @cfg {number} g_height height of the chart (respected by all elements in the set)
19062  @cfg {number} g_width width of the chart (respected by all elements in the set)
19063  @cfg {Object} title The title of the chart
19064     
19065  -{Array}  values
19066  -opts (object) options for the chart 
19067      o {
19068      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
19069      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
19070      o vgutter (number)
19071      o colors (array) colors be used repeatedly to plot the bars. If multicolumn bar is used each sequence of bars with use a different color.
19072      o stacked (boolean) whether or not to tread values as in a stacked bar chart
19073      o to
19074      o stretch (boolean)
19075      o }
19076  -opts (object) options for the pie
19077      o{
19078      o cut
19079      o startAngle (number)
19080      o endAngle (number)
19081      } 
19082  *
19083  * @constructor
19084  * Create a new Input
19085  * @param {Object} config The config object
19086  */
19087
19088 Roo.bootstrap.Graph = function(config){
19089     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
19090     
19091     this.addEvents({
19092         // img events
19093         /**
19094          * @event click
19095          * The img click event for the img.
19096          * @param {Roo.EventObject} e
19097          */
19098         "click" : true
19099     });
19100 };
19101
19102 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
19103     
19104     sm: 4,
19105     md: 5,
19106     graphtype: 'bar',
19107     g_height: 250,
19108     g_width: 400,
19109     g_x: 50,
19110     g_y: 50,
19111     g_r: 30,
19112     opts:{
19113         //g_colors: this.colors,
19114         g_type: 'soft',
19115         g_gutter: '20%'
19116
19117     },
19118     title : false,
19119
19120     getAutoCreate : function(){
19121         
19122         var cfg = {
19123             tag: 'div',
19124             html : null
19125         }
19126         
19127         
19128         return  cfg;
19129     },
19130
19131     onRender : function(ct,position){
19132         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
19133         this.raphael = Raphael(this.el.dom);
19134         
19135                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19136                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19137                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19138                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
19139                 /*
19140                 r.text(160, 10, "Single Series Chart").attr(txtattr);
19141                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
19142                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
19143                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
19144                 
19145                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
19146                 r.barchart(330, 10, 300, 220, data1);
19147                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
19148                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
19149                 */
19150                 
19151                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19152                 // r.barchart(30, 30, 560, 250,  xdata, {
19153                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
19154                 //     axis : "0 0 1 1",
19155                 //     axisxlabels :  xdata
19156                 //     //yvalues : cols,
19157                    
19158                 // });
19159 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19160 //        
19161 //        this.load(null,xdata,{
19162 //                axis : "0 0 1 1",
19163 //                axisxlabels :  xdata
19164 //                });
19165
19166     },
19167
19168     load : function(graphtype,xdata,opts){
19169         this.raphael.clear();
19170         if(!graphtype) {
19171             graphtype = this.graphtype;
19172         }
19173         if(!opts){
19174             opts = this.opts;
19175         }
19176         var r = this.raphael,
19177             fin = function () {
19178                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
19179             },
19180             fout = function () {
19181                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
19182             },
19183             pfin = function() {
19184                 this.sector.stop();
19185                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
19186
19187                 if (this.label) {
19188                     this.label[0].stop();
19189                     this.label[0].attr({ r: 7.5 });
19190                     this.label[1].attr({ "font-weight": 800 });
19191                 }
19192             },
19193             pfout = function() {
19194                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
19195
19196                 if (this.label) {
19197                     this.label[0].animate({ r: 5 }, 500, "bounce");
19198                     this.label[1].attr({ "font-weight": 400 });
19199                 }
19200             };
19201
19202         switch(graphtype){
19203             case 'bar':
19204                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19205                 break;
19206             case 'hbar':
19207                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19208                 break;
19209             case 'pie':
19210 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
19211 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
19212 //            
19213                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
19214                 
19215                 break;
19216
19217         }
19218         
19219         if(this.title){
19220             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
19221         }
19222         
19223     },
19224     
19225     setTitle: function(o)
19226     {
19227         this.title = o;
19228     },
19229     
19230     initEvents: function() {
19231         
19232         if(!this.href){
19233             this.el.on('click', this.onClick, this);
19234         }
19235     },
19236     
19237     onClick : function(e)
19238     {
19239         Roo.log('img onclick');
19240         this.fireEvent('click', this, e);
19241     }
19242    
19243 });
19244
19245  
19246 /*
19247  * - LGPL
19248  *
19249  * numberBox
19250  * 
19251  */
19252 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19253
19254 /**
19255  * @class Roo.bootstrap.dash.NumberBox
19256  * @extends Roo.bootstrap.Component
19257  * Bootstrap NumberBox class
19258  * @cfg {String} bgcolor Box background color, such as (aqua | green | yellow | red etc..) Default aqua
19259  * @cfg {String} headline Box headline
19260  * @cfg {String} content Box content
19261  * @cfg {String} icon Box icon
19262  * @cfg {String} footer Footer text
19263  * @cfg {String} fhref Footer href
19264  * 
19265  * @constructor
19266  * Create a new NumberBox
19267  * @param {Object} config The config object
19268  */
19269
19270
19271 Roo.bootstrap.dash.NumberBox = function(config){
19272     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
19273     
19274 };
19275
19276 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
19277     
19278     bgcolor : 'aqua',
19279     headline : '',
19280     content : '',
19281     icon : '',
19282     footer : '',
19283     fhref : '',
19284     ficon : '',
19285     
19286     getAutoCreate : function(){
19287         
19288         var cfg = {
19289             tag : 'div',
19290             cls : 'small-box bg-' + this.bgcolor,
19291             cn : [
19292                 {
19293                     tag : 'div',
19294                     cls : 'inner',
19295                     cn :[
19296                         {
19297                             tag : 'h3',
19298                             cls : 'roo-headline',
19299                             html : this.headline
19300                         },
19301                         {
19302                             tag : 'p',
19303                             cls : 'roo-content',
19304                             html : this.content
19305                         }
19306                     ]
19307                 }
19308             ]
19309         }
19310         
19311         if(this.icon){
19312             cfg.cn.push({
19313                 tag : 'div',
19314                 cls : 'icon',
19315                 cn :[
19316                     {
19317                         tag : 'i',
19318                         cls : 'ion ' + this.icon
19319                     }
19320                 ]
19321             });
19322         }
19323         
19324         if(this.footer){
19325             var footer = {
19326                 tag : 'a',
19327                 cls : 'small-box-footer',
19328                 href : this.fhref || '#',
19329                 html : this.footer
19330             };
19331             
19332             cfg.cn.push(footer);
19333             
19334         }
19335         
19336         return  cfg;
19337     },
19338
19339     onRender : function(ct,position){
19340         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
19341
19342
19343        
19344                 
19345     },
19346
19347     setHeadline: function (value)
19348     {
19349         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
19350     },
19351     
19352     setFooter: function (value, href)
19353     {
19354         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
19355         
19356         if(href){
19357             this.el.select('a.small-box-footer',true).first().attr('href', href);
19358         }
19359         
19360     },
19361
19362     setContent: function (value)
19363     {
19364         this.el.select('.roo-content',true).first().dom.innerHTML = value;
19365     },
19366
19367     initEvents: function() 
19368     {   
19369         
19370     }
19371     
19372 });
19373
19374  
19375 /*
19376  * - LGPL
19377  *
19378  * TabBox
19379  * 
19380  */
19381 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19382
19383 /**
19384  * @class Roo.bootstrap.dash.TabBox
19385  * @extends Roo.bootstrap.Component
19386  * Bootstrap TabBox class
19387  * @cfg {String} title Title of the TabBox
19388  * @cfg {String} icon Icon of the TabBox
19389  * 
19390  * @constructor
19391  * Create a new TabBox
19392  * @param {Object} config The config object
19393  */
19394
19395
19396 Roo.bootstrap.dash.TabBox = function(config){
19397     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
19398     this.addEvents({
19399         // raw events
19400         /**
19401          * @event addpane
19402          * When a pane is added
19403          * @param {Roo.bootstrap.dash.TabPane} pane
19404          */
19405         "addpane" : true
19406          
19407     });
19408 };
19409
19410 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
19411
19412     title : '',
19413     icon : false,
19414     
19415     getChildContainer : function()
19416     {
19417         return this.el.select('.tab-content', true).first();
19418     },
19419     
19420     getAutoCreate : function(){
19421         
19422         var header = {
19423             tag: 'li',
19424             cls: 'pull-left header',
19425             html: this.title,
19426             cn : []
19427         };
19428         
19429         if(this.icon){
19430             header.cn.push({
19431                 tag: 'i',
19432                 cls: 'fa ' + this.icon
19433             });
19434         }
19435         
19436         
19437         var cfg = {
19438             tag: 'div',
19439             cls: 'nav-tabs-custom',
19440             cn: [
19441                 {
19442                     tag: 'ul',
19443                     cls: 'nav nav-tabs pull-right',
19444                     cn: [
19445                         header
19446                     ]
19447                 },
19448                 {
19449                     tag: 'div',
19450                     cls: 'tab-content no-padding',
19451                     cn: []
19452                 }
19453             ]
19454         }
19455
19456         return  cfg;
19457     },
19458     initEvents : function()
19459     {
19460         //Roo.log('add add pane handler');
19461         this.on('addpane', this.onAddPane, this);
19462     },
19463      /**
19464      * Updates the box title
19465      * @param {String} html to set the title to.
19466      */
19467     setTitle : function(value)
19468     {
19469         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
19470     },
19471     onAddPane : function(pane)
19472     {
19473         //Roo.log('addpane');
19474         //Roo.log(pane);
19475         // tabs are rendere left to right..
19476         var ctr = this.el.select('.nav-tabs', true).first();
19477          
19478          
19479         var existing = ctr.select('.nav-tab',true);
19480         var qty = existing.getCount();;
19481         
19482         
19483         var tab = ctr.createChild({
19484             tag : 'li',
19485             cls : 'nav-tab' + (qty ? '' : ' active'),
19486             cn : [
19487                 {
19488                     tag : 'a',
19489                     href:'#',
19490                     html : pane.title
19491                 }
19492             ]
19493         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
19494         pane.tab = tab;
19495         
19496         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
19497         if (!qty) {
19498             pane.el.addClass('active');
19499         }
19500         
19501                 
19502     },
19503     onTabClick : function(ev,un,ob,pane)
19504     {
19505         //Roo.log('tab - prev default');
19506         ev.preventDefault();
19507         
19508         
19509         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
19510         pane.tab.addClass('active');
19511         //Roo.log(pane.title);
19512         this.getChildContainer().select('.tab-pane',true).removeClass('active');
19513         // technically we should have a deactivate event.. but maybe add later.
19514         // and it should not de-activate the selected tab...
19515         
19516         pane.el.addClass('active');
19517         pane.fireEvent('activate');
19518         
19519         
19520     }
19521     
19522     
19523 });
19524
19525  
19526 /*
19527  * - LGPL
19528  *
19529  * Tab pane
19530  * 
19531  */
19532 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19533 /**
19534  * @class Roo.bootstrap.TabPane
19535  * @extends Roo.bootstrap.Component
19536  * Bootstrap TabPane class
19537  * @cfg {Boolean} active (false | true) Default false
19538  * @cfg {String} title title of panel
19539
19540  * 
19541  * @constructor
19542  * Create a new TabPane
19543  * @param {Object} config The config object
19544  */
19545
19546 Roo.bootstrap.dash.TabPane = function(config){
19547     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
19548     
19549 };
19550
19551 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
19552     
19553     active : false,
19554     title : '',
19555     
19556     // the tabBox that this is attached to.
19557     tab : false,
19558      
19559     getAutoCreate : function() 
19560     {
19561         var cfg = {
19562             tag: 'div',
19563             cls: 'tab-pane'
19564         }
19565         
19566         if(this.active){
19567             cfg.cls += ' active';
19568         }
19569         
19570         return cfg;
19571     },
19572     initEvents  : function()
19573     {
19574         //Roo.log('trigger add pane handler');
19575         this.parent().fireEvent('addpane', this)
19576     },
19577     
19578      /**
19579      * Updates the tab title 
19580      * @param {String} html to set the title to.
19581      */
19582     setTitle: function(str)
19583     {
19584         if (!this.tab) {
19585             return;
19586         }
19587         this.title = str;
19588         this.tab.select('a'.true).first().dom.innerHTML = str;
19589         
19590     }
19591     
19592     
19593     
19594 });
19595
19596  
19597
19598
19599  /*
19600  * - LGPL
19601  *
19602  * menu
19603  * 
19604  */
19605 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
19606
19607 /**
19608  * @class Roo.bootstrap.menu.Menu
19609  * @extends Roo.bootstrap.Component
19610  * Bootstrap Menu class - container for Menu
19611  * @cfg {String} html Text of the menu
19612  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
19613  * @cfg {String} icon Font awesome icon
19614  * @cfg {String} pos Menu align to (top | bottom) default bottom
19615  * 
19616  * 
19617  * @constructor
19618  * Create a new Menu
19619  * @param {Object} config The config object
19620  */
19621
19622
19623 Roo.bootstrap.menu.Menu = function(config){
19624     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
19625     
19626     this.addEvents({
19627         /**
19628          * @event beforeshow
19629          * Fires before this menu is displayed
19630          * @param {Roo.bootstrap.menu.Menu} this
19631          */
19632         beforeshow : true,
19633         /**
19634          * @event beforehide
19635          * Fires before this menu is hidden
19636          * @param {Roo.bootstrap.menu.Menu} this
19637          */
19638         beforehide : true,
19639         /**
19640          * @event show
19641          * Fires after this menu is displayed
19642          * @param {Roo.bootstrap.menu.Menu} this
19643          */
19644         show : true,
19645         /**
19646          * @event hide
19647          * Fires after this menu is hidden
19648          * @param {Roo.bootstrap.menu.Menu} this
19649          */
19650         hide : true,
19651         /**
19652          * @event click
19653          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19654          * @param {Roo.bootstrap.menu.Menu} this
19655          * @param {Roo.EventObject} e
19656          */
19657         click : true
19658     });
19659     
19660 };
19661
19662 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
19663     
19664     submenu : false,
19665     html : '',
19666     weight : 'default',
19667     icon : false,
19668     pos : 'bottom',
19669     
19670     
19671     getChildContainer : function() {
19672         if(this.isSubMenu){
19673             return this.el;
19674         }
19675         
19676         return this.el.select('ul.dropdown-menu', true).first();  
19677     },
19678     
19679     getAutoCreate : function()
19680     {
19681         var text = [
19682             {
19683                 tag : 'span',
19684                 cls : 'roo-menu-text',
19685                 html : this.html
19686             }
19687         ];
19688         
19689         if(this.icon){
19690             text.unshift({
19691                 tag : 'i',
19692                 cls : 'fa ' + this.icon
19693             })
19694         }
19695         
19696         
19697         var cfg = {
19698             tag : 'div',
19699             cls : 'btn-group',
19700             cn : [
19701                 {
19702                     tag : 'button',
19703                     cls : 'dropdown-button btn btn-' + this.weight,
19704                     cn : text
19705                 },
19706                 {
19707                     tag : 'button',
19708                     cls : 'dropdown-toggle btn btn-' + this.weight,
19709                     cn : [
19710                         {
19711                             tag : 'span',
19712                             cls : 'caret'
19713                         }
19714                     ]
19715                 },
19716                 {
19717                     tag : 'ul',
19718                     cls : 'dropdown-menu'
19719                 }
19720             ]
19721             
19722         };
19723         
19724         if(this.pos == 'top'){
19725             cfg.cls += ' dropup';
19726         }
19727         
19728         if(this.isSubMenu){
19729             cfg = {
19730                 tag : 'ul',
19731                 cls : 'dropdown-menu'
19732             }
19733         }
19734         
19735         return cfg;
19736     },
19737     
19738     onRender : function(ct, position)
19739     {
19740         this.isSubMenu = ct.hasClass('dropdown-submenu');
19741         
19742         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
19743     },
19744     
19745     initEvents : function() 
19746     {
19747         if(this.isSubMenu){
19748             return;
19749         }
19750         
19751         this.hidden = true;
19752         
19753         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
19754         this.triggerEl.on('click', this.onTriggerPress, this);
19755         
19756         this.buttonEl = this.el.select('button.dropdown-button', true).first();
19757         this.buttonEl.on('click', this.onClick, this);
19758         
19759     },
19760     
19761     list : function()
19762     {
19763         if(this.isSubMenu){
19764             return this.el;
19765         }
19766         
19767         return this.el.select('ul.dropdown-menu', true).first();
19768     },
19769     
19770     onClick : function(e)
19771     {
19772         this.fireEvent("click", this, e);
19773     },
19774     
19775     onTriggerPress  : function(e)
19776     {   
19777         if (this.isVisible()) {
19778             this.hide();
19779         } else {
19780             this.show();
19781         }
19782     },
19783     
19784     isVisible : function(){
19785         return !this.hidden;
19786     },
19787     
19788     show : function()
19789     {
19790         this.fireEvent("beforeshow", this);
19791         
19792         this.hidden = false;
19793         this.el.addClass('open');
19794         
19795         Roo.get(document).on("mouseup", this.onMouseUp, this);
19796         
19797         this.fireEvent("show", this);
19798         
19799         
19800     },
19801     
19802     hide : function()
19803     {
19804         this.fireEvent("beforehide", this);
19805         
19806         this.hidden = true;
19807         this.el.removeClass('open');
19808         
19809         Roo.get(document).un("mouseup", this.onMouseUp);
19810         
19811         this.fireEvent("hide", this);
19812     },
19813     
19814     onMouseUp : function()
19815     {
19816         this.hide();
19817     }
19818     
19819 });
19820
19821  
19822  /*
19823  * - LGPL
19824  *
19825  * menu item
19826  * 
19827  */
19828 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
19829
19830 /**
19831  * @class Roo.bootstrap.menu.Item
19832  * @extends Roo.bootstrap.Component
19833  * Bootstrap MenuItem class
19834  * @cfg {Boolean} submenu (true | false) default false
19835  * @cfg {String} html text of the item
19836  * @cfg {String} href the link
19837  * @cfg {Boolean} disable (true | false) default false
19838  * @cfg {Boolean} preventDefault (true | false) default true
19839  * @cfg {String} icon Font awesome icon
19840  * @cfg {String} pos Submenu align to (left | right) default right 
19841  * 
19842  * 
19843  * @constructor
19844  * Create a new Item
19845  * @param {Object} config The config object
19846  */
19847
19848
19849 Roo.bootstrap.menu.Item = function(config){
19850     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
19851     this.addEvents({
19852         /**
19853          * @event mouseover
19854          * Fires when the mouse is hovering over this menu
19855          * @param {Roo.bootstrap.menu.Item} this
19856          * @param {Roo.EventObject} e
19857          */
19858         mouseover : true,
19859         /**
19860          * @event mouseout
19861          * Fires when the mouse exits this menu
19862          * @param {Roo.bootstrap.menu.Item} this
19863          * @param {Roo.EventObject} e
19864          */
19865         mouseout : true,
19866         // raw events
19867         /**
19868          * @event click
19869          * The raw click event for the entire grid.
19870          * @param {Roo.EventObject} e
19871          */
19872         click : true
19873     });
19874 };
19875
19876 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
19877     
19878     submenu : false,
19879     href : '',
19880     html : '',
19881     preventDefault: true,
19882     disable : false,
19883     icon : false,
19884     pos : 'right',
19885     
19886     getAutoCreate : function()
19887     {
19888         var text = [
19889             {
19890                 tag : 'span',
19891                 cls : 'roo-menu-item-text',
19892                 html : this.html
19893             }
19894         ];
19895         
19896         if(this.icon){
19897             text.unshift({
19898                 tag : 'i',
19899                 cls : 'fa ' + this.icon
19900             })
19901         }
19902         
19903         var cfg = {
19904             tag : 'li',
19905             cn : [
19906                 {
19907                     tag : 'a',
19908                     href : this.href || '#',
19909                     cn : text
19910                 }
19911             ]
19912         };
19913         
19914         if(this.disable){
19915             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
19916         }
19917         
19918         if(this.submenu){
19919             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
19920             
19921             if(this.pos == 'left'){
19922                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
19923             }
19924         }
19925         
19926         return cfg;
19927     },
19928     
19929     initEvents : function() 
19930     {
19931         this.el.on('mouseover', this.onMouseOver, this);
19932         this.el.on('mouseout', this.onMouseOut, this);
19933         
19934         this.el.select('a', true).first().on('click', this.onClick, this);
19935         
19936     },
19937     
19938     onClick : function(e)
19939     {
19940         if(this.preventDefault){
19941             e.preventDefault();
19942         }
19943         
19944         this.fireEvent("click", this, e);
19945     },
19946     
19947     onMouseOver : function(e)
19948     {
19949         if(this.submenu && this.pos == 'left'){
19950             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
19951         }
19952         
19953         this.fireEvent("mouseover", this, e);
19954     },
19955     
19956     onMouseOut : function(e)
19957     {
19958         this.fireEvent("mouseout", this, e);
19959     }
19960 });
19961
19962  
19963
19964  /*
19965  * - LGPL
19966  *
19967  * menu separator
19968  * 
19969  */
19970 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
19971
19972 /**
19973  * @class Roo.bootstrap.menu.Separator
19974  * @extends Roo.bootstrap.Component
19975  * Bootstrap Separator class
19976  * 
19977  * @constructor
19978  * Create a new Separator
19979  * @param {Object} config The config object
19980  */
19981
19982
19983 Roo.bootstrap.menu.Separator = function(config){
19984     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
19985 };
19986
19987 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
19988     
19989     getAutoCreate : function(){
19990         var cfg = {
19991             tag : 'li',
19992             cls: 'divider'
19993         };
19994         
19995         return cfg;
19996     }
19997    
19998 });
19999
20000  
20001
20002