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     indexOfNav : function()
3302     {
3303         
3304         var prev = false;
3305         Roo.each(this.navItems, function(v,i){
3306             
3307             if (v.isActive()) {
3308                 prev = i;
3309                 
3310             }
3311             
3312         });
3313         return prev;
3314     },
3315     /**
3316     * adds a Navigation item
3317     * @param {Roo.bootstrap.NavItem} the navitem to add
3318     */
3319     addItem : function(cfg)
3320     {
3321         var cn = new Roo.bootstrap.NavItem(cfg);
3322         this.register(cn);
3323         cn.parentId = this.id;
3324         cn.onRender(this.el, null);
3325         return cn;
3326     },
3327     /**
3328     * register a Navigation item
3329     * @param {Roo.bootstrap.NavItem} the navitem to add
3330     */
3331     register : function(item)
3332     {
3333         this.navItems.push( item);
3334         item.navId = this.navId;
3335     
3336     },
3337   
3338     
3339     getNavItem: function(tabId)
3340     {
3341         var ret = false;
3342         Roo.each(this.navItems, function(e) {
3343             if (e.tabId == tabId) {
3344                ret =  e;
3345                return false;
3346             }
3347             return true;
3348             
3349         });
3350         return ret;
3351     },
3352     
3353     setActiveNext : function()
3354     {
3355         var i = this.indexOfNav(this.getActive());
3356         if (i > this.navItems.length) {
3357             return;
3358         }
3359         this.setActiveItem(this.navItems[i+1]);
3360     },
3361     setActivePrev : function()
3362     {
3363         var i = this.indexOfNav(this.getActive());
3364         if (i  < 1) {
3365             return;
3366         }
3367         this.setActiveItem(this.navItems[i-1]);
3368     },
3369     clearWasActive : function(except) {
3370         Roo.each(this.navItems, function(e) {
3371             if (e.tabId != except.tabId && e.was_active) {
3372                e.was_active = false;
3373                return false;
3374             }
3375             return true;
3376             
3377         });
3378     },
3379     getWasActive : function ()
3380     {
3381         var r = false;
3382         Roo.each(this.navItems, function(e) {
3383             if (e.was_active) {
3384                r = e;
3385                return false;
3386             }
3387             return true;
3388             
3389         });
3390         return r;
3391     }
3392     
3393     
3394 });
3395
3396  
3397 Roo.apply(Roo.bootstrap.NavGroup, {
3398     
3399     groups: {},
3400      /**
3401     * register a Navigation Group
3402     * @param {Roo.bootstrap.NavGroup} the navgroup to add
3403     */
3404     register : function(navgrp)
3405     {
3406         this.groups[navgrp.navId] = navgrp;
3407         
3408     },
3409     /**
3410     * fetch a Navigation Group based on the navigation ID
3411     * @param {string} the navgroup to add
3412     * @returns {Roo.bootstrap.NavGroup} the navgroup 
3413     */
3414     get: function(navId) {
3415         if (typeof(this.groups[navId]) == 'undefined') {
3416             return false;
3417             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
3418         }
3419         return this.groups[navId] ;
3420     }
3421     
3422     
3423     
3424 });
3425
3426  /*
3427  * - LGPL
3428  *
3429  * row
3430  * 
3431  */
3432
3433 /**
3434  * @class Roo.bootstrap.NavItem
3435  * @extends Roo.bootstrap.Component
3436  * Bootstrap Navbar.NavItem class
3437  * @cfg {String} href  link to
3438  * @cfg {String} html content of button
3439  * @cfg {String} badge text inside badge
3440  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
3441  * @cfg {String} glyphicon name of glyphicon
3442  * @cfg {String} icon name of font awesome icon
3443  * @cfg {Boolean} active Is item active
3444  * @cfg {Boolean} disabled Is item disabled
3445  
3446  * @cfg {Boolean} preventDefault (true | false) default false
3447  * @cfg {String} tabId the tab that this item activates.
3448  * @cfg {String} tagtype (a|span) render as a href or span?
3449   
3450  * @constructor
3451  * Create a new Navbar Item
3452  * @param {Object} config The config object
3453  */
3454 Roo.bootstrap.NavItem = function(config){
3455     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
3456     this.addEvents({
3457         // raw events
3458         /**
3459          * @event click
3460          * The raw click event for the entire grid.
3461          * @param {Roo.EventObject} e
3462          */
3463         "click" : true,
3464          /**
3465             * @event changed
3466             * Fires when the active item active state changes
3467             * @param {Roo.bootstrap.NavItem} this
3468             * @param {boolean} state the new state
3469              
3470          */
3471         'changed': true
3472     });
3473    
3474 };
3475
3476 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
3477     
3478     href: false,
3479     html: '',
3480     badge: '',
3481     icon: false,
3482     glyphicon: false,
3483     active: false,
3484     preventDefault : false,
3485     tabId : false,
3486     tagtype : 'a',
3487     disabled : false,
3488     
3489     was_active : false,
3490     
3491     getAutoCreate : function(){
3492          
3493         var cfg = {
3494             tag: 'li',
3495             cls: 'nav-item'
3496             
3497         }
3498         if (this.active) {
3499             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
3500         }
3501         if (this.disabled) {
3502             cfg.cls += ' disabled';
3503         }
3504         
3505         if (this.href || this.html || this.glyphicon || this.icon) {
3506             cfg.cn = [
3507                 {
3508                     tag: this.tagtype,
3509                     href : this.href || "#",
3510                     html: this.html || ''
3511                 }
3512             ];
3513             
3514             if (this.icon) {
3515                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
3516             }
3517
3518             if(this.glyphicon) {
3519                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
3520             }
3521             
3522             if (this.menu) {
3523                 
3524                 cfg.cn[0].html += " <span class='caret'></span>";
3525              
3526             }
3527             
3528             if (this.badge !== '') {
3529                  
3530                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
3531             }
3532         }
3533         
3534         
3535         
3536         return cfg;
3537     },
3538     initEvents: function() {
3539        // Roo.log('init events?');
3540        // Roo.log(this.el.dom);
3541         if (typeof (this.menu) != 'undefined') {
3542             this.menu.parentType = this.xtype;
3543             this.menu.triggerEl = this.el;
3544             this.addxtype(Roo.apply({}, this.menu));
3545         }
3546
3547        
3548         this.el.select('a',true).on('click', this.onClick, this);
3549         // at this point parent should be available..
3550         this.parent().register(this);
3551     },
3552     
3553     onClick : function(e)
3554     {
3555          
3556         if(this.preventDefault){
3557             e.preventDefault();
3558         }
3559         if (this.disabled) {
3560             return;
3561         }
3562         Roo.log("fire event clicked");
3563         if(this.fireEvent('click', this, e) === false){
3564             return;
3565         };
3566         
3567         if (['tabs','pills'].indexOf(this.parent().type)!==-1) {
3568             if (typeof(this.parent().setActiveItem) !== 'undefined') {
3569                 this.parent().setActiveItem(this);
3570             }
3571         } 
3572     },
3573     
3574     isActive: function () {
3575         return this.active
3576     },
3577     setActive : function(state, fire, is_was_active)
3578     {
3579         if (this.active && !state & this.navId) {
3580             this.was_active = true;
3581             var nv = Roo.bootstrap.NavGroup.get(this.navId);
3582             if (nv) {
3583                 nv.clearWasActive(this);
3584             }
3585             
3586         }
3587         this.active = state;
3588         
3589         if (!state ) {
3590             this.el.removeClass('active');
3591         } else if (!this.el.hasClass('active')) {
3592             this.el.addClass('active');
3593         }
3594         if (fire) {
3595             this.fireEvent('changed', this, state);
3596         }
3597         
3598         // show a panel if it's registered and related..
3599         
3600         if (!this.navId || !this.tabId || !state || is_was_active) {
3601             return;
3602         }
3603         
3604         var tg = Roo.bootstrap.TabGroup.get(this.navId);
3605         if (!tg) {
3606             return;
3607         }
3608         var pan = tg.getPanelByName(this.tabId);
3609         if (!pan) {
3610             return;
3611         }
3612         // if we can not flip to new panel - go back to old nav highlight..
3613         if (false == tg.showPanel(pan)) {
3614             var nv = Roo.bootstrap.NavGroup.get(this.navId);
3615             if (nv) {
3616                 var onav = nv.getWasActive();
3617                 if (onav) {
3618                     onav.setActive(true, false, true);
3619                 }
3620             }
3621             
3622         }
3623         
3624         
3625         
3626     },
3627      // this should not be here...
3628     setDisabled : function(state)
3629     {
3630         this.disabled = state;
3631         if (!state ) {
3632             this.el.removeClass('disabled');
3633         } else if (!this.el.hasClass('disabled')) {
3634             this.el.addClass('disabled');
3635         }
3636         
3637     }
3638 });
3639  
3640
3641  /*
3642  * - LGPL
3643  *
3644  * sidebar item
3645  *
3646  *  li
3647  *    <span> icon </span>
3648  *    <span> text </span>
3649  *    <span>badge </span>
3650  */
3651
3652 /**
3653  * @class Roo.bootstrap.NavSidebarItem
3654  * @extends Roo.bootstrap.NavItem
3655  * Bootstrap Navbar.NavSidebarItem class
3656  * @constructor
3657  * Create a new Navbar Button
3658  * @param {Object} config The config object
3659  */
3660 Roo.bootstrap.NavSidebarItem = function(config){
3661     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
3662     this.addEvents({
3663         // raw events
3664         /**
3665          * @event click
3666          * The raw click event for the entire grid.
3667          * @param {Roo.EventObject} e
3668          */
3669         "click" : true,
3670          /**
3671             * @event changed
3672             * Fires when the active item active state changes
3673             * @param {Roo.bootstrap.NavSidebarItem} this
3674             * @param {boolean} state the new state
3675              
3676          */
3677         'changed': true
3678     });
3679    
3680 };
3681
3682 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
3683     
3684     
3685     getAutoCreate : function(){
3686         
3687         
3688         var a = {
3689                 tag: 'a',
3690                 href : this.href || '#',
3691                 cls: '',
3692                 html : '',
3693                 cn : []
3694         };
3695         var cfg = {
3696             tag: 'li',
3697             cls: '',
3698             cn: [ a ]
3699         }
3700         var span = {
3701             tag: 'span',
3702             html : this.html || ''
3703         }
3704         
3705         
3706         if (this.active) {
3707             cfg.cls += ' active';
3708         }
3709         
3710         // left icon..
3711         if (this.glyphicon || this.icon) {
3712             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
3713             a.cn.push({ tag : 'i', cls : c }) ;
3714         }
3715         // html..
3716         a.cn.push(span);
3717         // then badge..
3718         if (this.badge !== '') {
3719             a.cn.push({ tag: 'span',  cls : 'badge pull-right ' + (this.badgecls || ''), html: this.badge }); 
3720         }
3721         // fi
3722         if (this.menu) {
3723             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
3724             a.cls += 'dropdown-toggle treeview' ;
3725             
3726         }
3727         
3728         
3729         
3730         return cfg;
3731          
3732            
3733     }
3734    
3735      
3736  
3737 });
3738  
3739
3740  /*
3741  * - LGPL
3742  *
3743  * row
3744  * 
3745  */
3746
3747 /**
3748  * @class Roo.bootstrap.Row
3749  * @extends Roo.bootstrap.Component
3750  * Bootstrap Row class (contains columns...)
3751  * 
3752  * @constructor
3753  * Create a new Row
3754  * @param {Object} config The config object
3755  */
3756
3757 Roo.bootstrap.Row = function(config){
3758     Roo.bootstrap.Row.superclass.constructor.call(this, config);
3759 };
3760
3761 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
3762     
3763     getAutoCreate : function(){
3764        return {
3765             cls: 'row clearfix'
3766        };
3767     }
3768     
3769     
3770 });
3771
3772  
3773
3774  /*
3775  * - LGPL
3776  *
3777  * element
3778  * 
3779  */
3780
3781 /**
3782  * @class Roo.bootstrap.Element
3783  * @extends Roo.bootstrap.Component
3784  * Bootstrap Element class
3785  * @cfg {String} html contents of the element
3786  * @cfg {String} tag tag of the element
3787  * @cfg {String} cls class of the element
3788  * 
3789  * @constructor
3790  * Create a new Element
3791  * @param {Object} config The config object
3792  */
3793
3794 Roo.bootstrap.Element = function(config){
3795     Roo.bootstrap.Element.superclass.constructor.call(this, config);
3796 };
3797
3798 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
3799     
3800     tag: 'div',
3801     cls: '',
3802     html: '',
3803      
3804     
3805     getAutoCreate : function(){
3806         
3807         var cfg = {
3808             tag: this.tag,
3809             cls: this.cls,
3810             html: this.html
3811         }
3812         
3813         
3814         
3815         return cfg;
3816     }
3817    
3818 });
3819
3820  
3821
3822  /*
3823  * - LGPL
3824  *
3825  * pagination
3826  * 
3827  */
3828
3829 /**
3830  * @class Roo.bootstrap.Pagination
3831  * @extends Roo.bootstrap.Component
3832  * Bootstrap Pagination class
3833  * @cfg {String} size xs | sm | md | lg
3834  * @cfg {Boolean} inverse false | true
3835  * 
3836  * @constructor
3837  * Create a new Pagination
3838  * @param {Object} config The config object
3839  */
3840
3841 Roo.bootstrap.Pagination = function(config){
3842     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
3843 };
3844
3845 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
3846     
3847     cls: false,
3848     size: false,
3849     inverse: false,
3850     
3851     getAutoCreate : function(){
3852         var cfg = {
3853             tag: 'ul',
3854                 cls: 'pagination'
3855         };
3856         if (this.inverse) {
3857             cfg.cls += ' inverse';
3858         }
3859         if (this.html) {
3860             cfg.html=this.html;
3861         }
3862         if (this.cls) {
3863             cfg.cls += " " + this.cls;
3864         }
3865         return cfg;
3866     }
3867    
3868 });
3869
3870  
3871
3872  /*
3873  * - LGPL
3874  *
3875  * Pagination item
3876  * 
3877  */
3878
3879
3880 /**
3881  * @class Roo.bootstrap.PaginationItem
3882  * @extends Roo.bootstrap.Component
3883  * Bootstrap PaginationItem class
3884  * @cfg {String} html text
3885  * @cfg {String} href the link
3886  * @cfg {Boolean} preventDefault (true | false) default true
3887  * @cfg {Boolean} active (true | false) default false
3888  * 
3889  * 
3890  * @constructor
3891  * Create a new PaginationItem
3892  * @param {Object} config The config object
3893  */
3894
3895
3896 Roo.bootstrap.PaginationItem = function(config){
3897     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
3898     this.addEvents({
3899         // raw events
3900         /**
3901          * @event click
3902          * The raw click event for the entire grid.
3903          * @param {Roo.EventObject} e
3904          */
3905         "click" : true
3906     });
3907 };
3908
3909 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
3910     
3911     href : false,
3912     html : false,
3913     preventDefault: true,
3914     active : false,
3915     cls : false,
3916     
3917     getAutoCreate : function(){
3918         var cfg= {
3919             tag: 'li',
3920             cn: [
3921                 {
3922                     tag : 'a',
3923                     href : this.href ? this.href : '#',
3924                     html : this.html ? this.html : ''
3925                 }
3926             ]
3927         };
3928         
3929         if(this.cls){
3930             cfg.cls = this.cls;
3931         }
3932         
3933         if(this.active){
3934             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
3935         }
3936         
3937         return cfg;
3938     },
3939     
3940     initEvents: function() {
3941         
3942         this.el.on('click', this.onClick, this);
3943         
3944     },
3945     onClick : function(e)
3946     {
3947         Roo.log('PaginationItem on click ');
3948         if(this.preventDefault){
3949             e.preventDefault();
3950         }
3951         
3952         this.fireEvent('click', this, e);
3953     }
3954    
3955 });
3956
3957  
3958
3959  /*
3960  * - LGPL
3961  *
3962  * slider
3963  * 
3964  */
3965
3966
3967 /**
3968  * @class Roo.bootstrap.Slider
3969  * @extends Roo.bootstrap.Component
3970  * Bootstrap Slider class
3971  *    
3972  * @constructor
3973  * Create a new Slider
3974  * @param {Object} config The config object
3975  */
3976
3977 Roo.bootstrap.Slider = function(config){
3978     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
3979 };
3980
3981 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
3982     
3983     getAutoCreate : function(){
3984         
3985         var cfg = {
3986             tag: 'div',
3987             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
3988             cn: [
3989                 {
3990                     tag: 'a',
3991                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
3992                 }
3993             ]
3994         }
3995         
3996         return cfg;
3997     }
3998    
3999 });
4000
4001  /*
4002  * Based on:
4003  * Ext JS Library 1.1.1
4004  * Copyright(c) 2006-2007, Ext JS, LLC.
4005  *
4006  * Originally Released Under LGPL - original licence link has changed is not relivant.
4007  *
4008  * Fork - LGPL
4009  * <script type="text/javascript">
4010  */
4011  
4012
4013 /**
4014  * @class Roo.grid.ColumnModel
4015  * @extends Roo.util.Observable
4016  * This is the default implementation of a ColumnModel used by the Grid. It defines
4017  * the columns in the grid.
4018  * <br>Usage:<br>
4019  <pre><code>
4020  var colModel = new Roo.grid.ColumnModel([
4021         {header: "Ticker", width: 60, sortable: true, locked: true},
4022         {header: "Company Name", width: 150, sortable: true},
4023         {header: "Market Cap.", width: 100, sortable: true},
4024         {header: "$ Sales", width: 100, sortable: true, renderer: money},
4025         {header: "Employees", width: 100, sortable: true, resizable: false}
4026  ]);
4027  </code></pre>
4028  * <p>
4029  
4030  * The config options listed for this class are options which may appear in each
4031  * individual column definition.
4032  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4033  * @constructor
4034  * @param {Object} config An Array of column config objects. See this class's
4035  * config objects for details.
4036 */
4037 Roo.grid.ColumnModel = function(config){
4038         /**
4039      * The config passed into the constructor
4040      */
4041     this.config = config;
4042     this.lookup = {};
4043
4044     // if no id, create one
4045     // if the column does not have a dataIndex mapping,
4046     // map it to the order it is in the config
4047     for(var i = 0, len = config.length; i < len; i++){
4048         var c = config[i];
4049         if(typeof c.dataIndex == "undefined"){
4050             c.dataIndex = i;
4051         }
4052         if(typeof c.renderer == "string"){
4053             c.renderer = Roo.util.Format[c.renderer];
4054         }
4055         if(typeof c.id == "undefined"){
4056             c.id = Roo.id();
4057         }
4058         if(c.editor && c.editor.xtype){
4059             c.editor  = Roo.factory(c.editor, Roo.grid);
4060         }
4061         if(c.editor && c.editor.isFormField){
4062             c.editor = new Roo.grid.GridEditor(c.editor);
4063         }
4064         this.lookup[c.id] = c;
4065     }
4066
4067     /**
4068      * The width of columns which have no width specified (defaults to 100)
4069      * @type Number
4070      */
4071     this.defaultWidth = 100;
4072
4073     /**
4074      * Default sortable of columns which have no sortable specified (defaults to false)
4075      * @type Boolean
4076      */
4077     this.defaultSortable = false;
4078
4079     this.addEvents({
4080         /**
4081              * @event widthchange
4082              * Fires when the width of a column changes.
4083              * @param {ColumnModel} this
4084              * @param {Number} columnIndex The column index
4085              * @param {Number} newWidth The new width
4086              */
4087             "widthchange": true,
4088         /**
4089              * @event headerchange
4090              * Fires when the text of a header changes.
4091              * @param {ColumnModel} this
4092              * @param {Number} columnIndex The column index
4093              * @param {Number} newText The new header text
4094              */
4095             "headerchange": true,
4096         /**
4097              * @event hiddenchange
4098              * Fires when a column is hidden or "unhidden".
4099              * @param {ColumnModel} this
4100              * @param {Number} columnIndex The column index
4101              * @param {Boolean} hidden true if hidden, false otherwise
4102              */
4103             "hiddenchange": true,
4104             /**
4105          * @event columnmoved
4106          * Fires when a column is moved.
4107          * @param {ColumnModel} this
4108          * @param {Number} oldIndex
4109          * @param {Number} newIndex
4110          */
4111         "columnmoved" : true,
4112         /**
4113          * @event columlockchange
4114          * Fires when a column's locked state is changed
4115          * @param {ColumnModel} this
4116          * @param {Number} colIndex
4117          * @param {Boolean} locked true if locked
4118          */
4119         "columnlockchange" : true
4120     });
4121     Roo.grid.ColumnModel.superclass.constructor.call(this);
4122 };
4123 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4124     /**
4125      * @cfg {String} header The header text to display in the Grid view.
4126      */
4127     /**
4128      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4129      * {@link Roo.data.Record} definition from which to draw the column's value. If not
4130      * specified, the column's index is used as an index into the Record's data Array.
4131      */
4132     /**
4133      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4134      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4135      */
4136     /**
4137      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4138      * Defaults to the value of the {@link #defaultSortable} property.
4139      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4140      */
4141     /**
4142      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
4143      */
4144     /**
4145      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
4146      */
4147     /**
4148      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4149      */
4150     /**
4151      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4152      */
4153     /**
4154      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4155      * given the cell's data value. See {@link #setRenderer}. If not specified, the
4156      * default renderer uses the raw data value. If an object is returned (bootstrap only)
4157      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4158      */
4159        /**
4160      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
4161      */
4162     /**
4163      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
4164      */
4165
4166     /**
4167      * Returns the id of the column at the specified index.
4168      * @param {Number} index The column index
4169      * @return {String} the id
4170      */
4171     getColumnId : function(index){
4172         return this.config[index].id;
4173     },
4174
4175     /**
4176      * Returns the column for a specified id.
4177      * @param {String} id The column id
4178      * @return {Object} the column
4179      */
4180     getColumnById : function(id){
4181         return this.lookup[id];
4182     },
4183
4184     
4185     /**
4186      * Returns the column for a specified dataIndex.
4187      * @param {String} dataIndex The column dataIndex
4188      * @return {Object|Boolean} the column or false if not found
4189      */
4190     getColumnByDataIndex: function(dataIndex){
4191         var index = this.findColumnIndex(dataIndex);
4192         return index > -1 ? this.config[index] : false;
4193     },
4194     
4195     /**
4196      * Returns the index for a specified column id.
4197      * @param {String} id The column id
4198      * @return {Number} the index, or -1 if not found
4199      */
4200     getIndexById : function(id){
4201         for(var i = 0, len = this.config.length; i < len; i++){
4202             if(this.config[i].id == id){
4203                 return i;
4204             }
4205         }
4206         return -1;
4207     },
4208     
4209     /**
4210      * Returns the index for a specified column dataIndex.
4211      * @param {String} dataIndex The column dataIndex
4212      * @return {Number} the index, or -1 if not found
4213      */
4214     
4215     findColumnIndex : function(dataIndex){
4216         for(var i = 0, len = this.config.length; i < len; i++){
4217             if(this.config[i].dataIndex == dataIndex){
4218                 return i;
4219             }
4220         }
4221         return -1;
4222     },
4223     
4224     
4225     moveColumn : function(oldIndex, newIndex){
4226         var c = this.config[oldIndex];
4227         this.config.splice(oldIndex, 1);
4228         this.config.splice(newIndex, 0, c);
4229         this.dataMap = null;
4230         this.fireEvent("columnmoved", this, oldIndex, newIndex);
4231     },
4232
4233     isLocked : function(colIndex){
4234         return this.config[colIndex].locked === true;
4235     },
4236
4237     setLocked : function(colIndex, value, suppressEvent){
4238         if(this.isLocked(colIndex) == value){
4239             return;
4240         }
4241         this.config[colIndex].locked = value;
4242         if(!suppressEvent){
4243             this.fireEvent("columnlockchange", this, colIndex, value);
4244         }
4245     },
4246
4247     getTotalLockedWidth : function(){
4248         var totalWidth = 0;
4249         for(var i = 0; i < this.config.length; i++){
4250             if(this.isLocked(i) && !this.isHidden(i)){
4251                 this.totalWidth += this.getColumnWidth(i);
4252             }
4253         }
4254         return totalWidth;
4255     },
4256
4257     getLockedCount : function(){
4258         for(var i = 0, len = this.config.length; i < len; i++){
4259             if(!this.isLocked(i)){
4260                 return i;
4261             }
4262         }
4263     },
4264
4265     /**
4266      * Returns the number of columns.
4267      * @return {Number}
4268      */
4269     getColumnCount : function(visibleOnly){
4270         if(visibleOnly === true){
4271             var c = 0;
4272             for(var i = 0, len = this.config.length; i < len; i++){
4273                 if(!this.isHidden(i)){
4274                     c++;
4275                 }
4276             }
4277             return c;
4278         }
4279         return this.config.length;
4280     },
4281
4282     /**
4283      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
4284      * @param {Function} fn
4285      * @param {Object} scope (optional)
4286      * @return {Array} result
4287      */
4288     getColumnsBy : function(fn, scope){
4289         var r = [];
4290         for(var i = 0, len = this.config.length; i < len; i++){
4291             var c = this.config[i];
4292             if(fn.call(scope||this, c, i) === true){
4293                 r[r.length] = c;
4294             }
4295         }
4296         return r;
4297     },
4298
4299     /**
4300      * Returns true if the specified column is sortable.
4301      * @param {Number} col The column index
4302      * @return {Boolean}
4303      */
4304     isSortable : function(col){
4305         if(typeof this.config[col].sortable == "undefined"){
4306             return this.defaultSortable;
4307         }
4308         return this.config[col].sortable;
4309     },
4310
4311     /**
4312      * Returns the rendering (formatting) function defined for the column.
4313      * @param {Number} col The column index.
4314      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
4315      */
4316     getRenderer : function(col){
4317         if(!this.config[col].renderer){
4318             return Roo.grid.ColumnModel.defaultRenderer;
4319         }
4320         return this.config[col].renderer;
4321     },
4322
4323     /**
4324      * Sets the rendering (formatting) function for a column.
4325      * @param {Number} col The column index
4326      * @param {Function} fn The function to use to process the cell's raw data
4327      * to return HTML markup for the grid view. The render function is called with
4328      * the following parameters:<ul>
4329      * <li>Data value.</li>
4330      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
4331      * <li>css A CSS style string to apply to the table cell.</li>
4332      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
4333      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
4334      * <li>Row index</li>
4335      * <li>Column index</li>
4336      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
4337      */
4338     setRenderer : function(col, fn){
4339         this.config[col].renderer = fn;
4340     },
4341
4342     /**
4343      * Returns the width for the specified column.
4344      * @param {Number} col The column index
4345      * @return {Number}
4346      */
4347     getColumnWidth : function(col){
4348         return this.config[col].width * 1 || this.defaultWidth;
4349     },
4350
4351     /**
4352      * Sets the width for a column.
4353      * @param {Number} col The column index
4354      * @param {Number} width The new width
4355      */
4356     setColumnWidth : function(col, width, suppressEvent){
4357         this.config[col].width = width;
4358         this.totalWidth = null;
4359         if(!suppressEvent){
4360              this.fireEvent("widthchange", this, col, width);
4361         }
4362     },
4363
4364     /**
4365      * Returns the total width of all columns.
4366      * @param {Boolean} includeHidden True to include hidden column widths
4367      * @return {Number}
4368      */
4369     getTotalWidth : function(includeHidden){
4370         if(!this.totalWidth){
4371             this.totalWidth = 0;
4372             for(var i = 0, len = this.config.length; i < len; i++){
4373                 if(includeHidden || !this.isHidden(i)){
4374                     this.totalWidth += this.getColumnWidth(i);
4375                 }
4376             }
4377         }
4378         return this.totalWidth;
4379     },
4380
4381     /**
4382      * Returns the header for the specified column.
4383      * @param {Number} col The column index
4384      * @return {String}
4385      */
4386     getColumnHeader : function(col){
4387         return this.config[col].header;
4388     },
4389
4390     /**
4391      * Sets the header for a column.
4392      * @param {Number} col The column index
4393      * @param {String} header The new header
4394      */
4395     setColumnHeader : function(col, header){
4396         this.config[col].header = header;
4397         this.fireEvent("headerchange", this, col, header);
4398     },
4399
4400     /**
4401      * Returns the tooltip for the specified column.
4402      * @param {Number} col The column index
4403      * @return {String}
4404      */
4405     getColumnTooltip : function(col){
4406             return this.config[col].tooltip;
4407     },
4408     /**
4409      * Sets the tooltip for a column.
4410      * @param {Number} col The column index
4411      * @param {String} tooltip The new tooltip
4412      */
4413     setColumnTooltip : function(col, tooltip){
4414             this.config[col].tooltip = tooltip;
4415     },
4416
4417     /**
4418      * Returns the dataIndex for the specified column.
4419      * @param {Number} col The column index
4420      * @return {Number}
4421      */
4422     getDataIndex : function(col){
4423         return this.config[col].dataIndex;
4424     },
4425
4426     /**
4427      * Sets the dataIndex for a column.
4428      * @param {Number} col The column index
4429      * @param {Number} dataIndex The new dataIndex
4430      */
4431     setDataIndex : function(col, dataIndex){
4432         this.config[col].dataIndex = dataIndex;
4433     },
4434
4435     
4436     
4437     /**
4438      * Returns true if the cell is editable.
4439      * @param {Number} colIndex The column index
4440      * @param {Number} rowIndex The row index
4441      * @return {Boolean}
4442      */
4443     isCellEditable : function(colIndex, rowIndex){
4444         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
4445     },
4446
4447     /**
4448      * Returns the editor defined for the cell/column.
4449      * return false or null to disable editing.
4450      * @param {Number} colIndex The column index
4451      * @param {Number} rowIndex The row index
4452      * @return {Object}
4453      */
4454     getCellEditor : function(colIndex, rowIndex){
4455         return this.config[colIndex].editor;
4456     },
4457
4458     /**
4459      * Sets if a column is editable.
4460      * @param {Number} col The column index
4461      * @param {Boolean} editable True if the column is editable
4462      */
4463     setEditable : function(col, editable){
4464         this.config[col].editable = editable;
4465     },
4466
4467
4468     /**
4469      * Returns true if the column is hidden.
4470      * @param {Number} colIndex The column index
4471      * @return {Boolean}
4472      */
4473     isHidden : function(colIndex){
4474         return this.config[colIndex].hidden;
4475     },
4476
4477
4478     /**
4479      * Returns true if the column width cannot be changed
4480      */
4481     isFixed : function(colIndex){
4482         return this.config[colIndex].fixed;
4483     },
4484
4485     /**
4486      * Returns true if the column can be resized
4487      * @return {Boolean}
4488      */
4489     isResizable : function(colIndex){
4490         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
4491     },
4492     /**
4493      * Sets if a column is hidden.
4494      * @param {Number} colIndex The column index
4495      * @param {Boolean} hidden True if the column is hidden
4496      */
4497     setHidden : function(colIndex, hidden){
4498         this.config[colIndex].hidden = hidden;
4499         this.totalWidth = null;
4500         this.fireEvent("hiddenchange", this, colIndex, hidden);
4501     },
4502
4503     /**
4504      * Sets the editor for a column.
4505      * @param {Number} col The column index
4506      * @param {Object} editor The editor object
4507      */
4508     setEditor : function(col, editor){
4509         this.config[col].editor = editor;
4510     }
4511 });
4512
4513 Roo.grid.ColumnModel.defaultRenderer = function(value){
4514         if(typeof value == "string" && value.length < 1){
4515             return "&#160;";
4516         }
4517         return value;
4518 };
4519
4520 // Alias for backwards compatibility
4521 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
4522 /*
4523  * Based on:
4524  * Ext JS Library 1.1.1
4525  * Copyright(c) 2006-2007, Ext JS, LLC.
4526  *
4527  * Originally Released Under LGPL - original licence link has changed is not relivant.
4528  *
4529  * Fork - LGPL
4530  * <script type="text/javascript">
4531  */
4532  
4533 /**
4534  * @class Roo.LoadMask
4535  * A simple utility class for generically masking elements while loading data.  If the element being masked has
4536  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
4537  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
4538  * element's UpdateManager load indicator and will be destroyed after the initial load.
4539  * @constructor
4540  * Create a new LoadMask
4541  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
4542  * @param {Object} config The config object
4543  */
4544 Roo.LoadMask = function(el, config){
4545     this.el = Roo.get(el);
4546     Roo.apply(this, config);
4547     if(this.store){
4548         this.store.on('beforeload', this.onBeforeLoad, this);
4549         this.store.on('load', this.onLoad, this);
4550         this.store.on('loadexception', this.onLoadException, this);
4551         this.removeMask = false;
4552     }else{
4553         var um = this.el.getUpdateManager();
4554         um.showLoadIndicator = false; // disable the default indicator
4555         um.on('beforeupdate', this.onBeforeLoad, this);
4556         um.on('update', this.onLoad, this);
4557         um.on('failure', this.onLoad, this);
4558         this.removeMask = true;
4559     }
4560 };
4561
4562 Roo.LoadMask.prototype = {
4563     /**
4564      * @cfg {Boolean} removeMask
4565      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
4566      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
4567      */
4568     /**
4569      * @cfg {String} msg
4570      * The text to display in a centered loading message box (defaults to 'Loading...')
4571      */
4572     msg : 'Loading...',
4573     /**
4574      * @cfg {String} msgCls
4575      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
4576      */
4577     msgCls : 'x-mask-loading',
4578
4579     /**
4580      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
4581      * @type Boolean
4582      */
4583     disabled: false,
4584
4585     /**
4586      * Disables the mask to prevent it from being displayed
4587      */
4588     disable : function(){
4589        this.disabled = true;
4590     },
4591
4592     /**
4593      * Enables the mask so that it can be displayed
4594      */
4595     enable : function(){
4596         this.disabled = false;
4597     },
4598     
4599     onLoadException : function()
4600     {
4601         Roo.log(arguments);
4602         
4603         if (typeof(arguments[3]) != 'undefined') {
4604             Roo.MessageBox.alert("Error loading",arguments[3]);
4605         } 
4606         /*
4607         try {
4608             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
4609                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
4610             }   
4611         } catch(e) {
4612             
4613         }
4614         */
4615     
4616         
4617         
4618         this.el.unmask(this.removeMask);
4619     },
4620     // private
4621     onLoad : function()
4622     {
4623         this.el.unmask(this.removeMask);
4624     },
4625
4626     // private
4627     onBeforeLoad : function(){
4628         if(!this.disabled){
4629             this.el.mask(this.msg, this.msgCls);
4630         }
4631     },
4632
4633     // private
4634     destroy : function(){
4635         if(this.store){
4636             this.store.un('beforeload', this.onBeforeLoad, this);
4637             this.store.un('load', this.onLoad, this);
4638             this.store.un('loadexception', this.onLoadException, this);
4639         }else{
4640             var um = this.el.getUpdateManager();
4641             um.un('beforeupdate', this.onBeforeLoad, this);
4642             um.un('update', this.onLoad, this);
4643             um.un('failure', this.onLoad, this);
4644         }
4645     }
4646 };/*
4647  * - LGPL
4648  *
4649  * table
4650  * 
4651  */
4652
4653 /**
4654  * @class Roo.bootstrap.Table
4655  * @extends Roo.bootstrap.Component
4656  * Bootstrap Table class
4657  * @cfg {String} cls table class
4658  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
4659  * @cfg {String} bgcolor Specifies the background color for a table
4660  * @cfg {Number} border Specifies whether the table cells should have borders or not
4661  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
4662  * @cfg {Number} cellspacing Specifies the space between cells
4663  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
4664  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
4665  * @cfg {String} sortable Specifies that the table should be sortable
4666  * @cfg {String} summary Specifies a summary of the content of a table
4667  * @cfg {Number} width Specifies the width of a table
4668  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
4669  * 
4670  * @cfg {boolean} striped Should the rows be alternative striped
4671  * @cfg {boolean} bordered Add borders to the table
4672  * @cfg {boolean} hover Add hover highlighting
4673  * @cfg {boolean} condensed Format condensed
4674  * @cfg {boolean} responsive Format condensed
4675  * @cfg {Boolean} loadMask (true|false) default false
4676  * @cfg {Boolean} tfoot (true|false) generate tfoot, default true
4677  * @cfg {Boolean} thead (true|false) generate thead, default true
4678  * @cfg {Boolean} RowSelection (true|false) default false
4679  * @cfg {Boolean} CellSelection (true|false) default false
4680  *
4681  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
4682  
4683  * 
4684  * @constructor
4685  * Create a new Table
4686  * @param {Object} config The config object
4687  */
4688
4689 Roo.bootstrap.Table = function(config){
4690     Roo.bootstrap.Table.superclass.constructor.call(this, config);
4691     
4692     if (this.sm) {
4693         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
4694         this.sm = this.selModel;
4695         this.sm.xmodule = this.xmodule || false;
4696     }
4697     if (this.cm && typeof(this.cm.config) == 'undefined') {
4698         this.colModel = new Roo.grid.ColumnModel(this.cm);
4699         this.cm = this.colModel;
4700         this.cm.xmodule = this.xmodule || false;
4701     }
4702     if (this.store) {
4703         this.store= Roo.factory(this.store, Roo.data);
4704         this.ds = this.store;
4705         this.ds.xmodule = this.xmodule || false;
4706          
4707     }
4708     if (this.footer && this.store) {
4709         this.footer.dataSource = this.ds;
4710         this.footer = Roo.factory(this.footer);
4711     }
4712     
4713     /** @private */
4714     this.addEvents({
4715         /**
4716          * @event cellclick
4717          * Fires when a cell is clicked
4718          * @param {Roo.bootstrap.Table} this
4719          * @param {Roo.Element} el
4720          * @param {Number} rowIndex
4721          * @param {Number} columnIndex
4722          * @param {Roo.EventObject} e
4723          */
4724         "cellclick" : true,
4725         /**
4726          * @event celldblclick
4727          * Fires when a cell is double clicked
4728          * @param {Roo.bootstrap.Table} this
4729          * @param {Roo.Element} el
4730          * @param {Number} rowIndex
4731          * @param {Number} columnIndex
4732          * @param {Roo.EventObject} e
4733          */
4734         "celldblclick" : true,
4735         /**
4736          * @event rowclick
4737          * Fires when a row is clicked
4738          * @param {Roo.bootstrap.Table} this
4739          * @param {Roo.Element} el
4740          * @param {Number} rowIndex
4741          * @param {Roo.EventObject} e
4742          */
4743         "rowclick" : true,
4744         /**
4745          * @event rowdblclick
4746          * Fires when a row is double clicked
4747          * @param {Roo.bootstrap.Table} this
4748          * @param {Roo.Element} el
4749          * @param {Number} rowIndex
4750          * @param {Roo.EventObject} e
4751          */
4752         "rowdblclick" : true,
4753         /**
4754          * @event mouseover
4755          * Fires when a mouseover occur
4756          * @param {Roo.bootstrap.Table} this
4757          * @param {Roo.Element} el
4758          * @param {Number} rowIndex
4759          * @param {Number} columnIndex
4760          * @param {Roo.EventObject} e
4761          */
4762         "mouseover" : true,
4763         /**
4764          * @event mouseout
4765          * Fires when a mouseout occur
4766          * @param {Roo.bootstrap.Table} this
4767          * @param {Roo.Element} el
4768          * @param {Number} rowIndex
4769          * @param {Number} columnIndex
4770          * @param {Roo.EventObject} e
4771          */
4772         "mouseout" : true,
4773         /**
4774          * @event rowclass
4775          * Fires when a row is rendered, so you can change add a style to it.
4776          * @param {Roo.bootstrap.Table} this
4777          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
4778          */
4779         'rowclass' : true
4780         
4781     });
4782 };
4783
4784 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
4785     
4786     cls: false,
4787     align: false,
4788     bgcolor: false,
4789     border: false,
4790     cellpadding: false,
4791     cellspacing: false,
4792     frame: false,
4793     rules: false,
4794     sortable: false,
4795     summary: false,
4796     width: false,
4797     striped : false,
4798     bordered: false,
4799     hover:  false,
4800     condensed : false,
4801     responsive : false,
4802     sm : false,
4803     cm : false,
4804     store : false,
4805     loadMask : false,
4806     tfoot : true,
4807     thead : true,
4808     RowSelection : false,
4809     CellSelection : false,
4810     layout : false,
4811     
4812     // Roo.Element - the tbody
4813     mainBody: false, 
4814     
4815     getAutoCreate : function(){
4816         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
4817         
4818         cfg = {
4819             tag: 'table',
4820             cls : 'table',
4821             cn : []
4822         }
4823             
4824         if (this.striped) {
4825             cfg.cls += ' table-striped';
4826         }
4827         
4828         if (this.hover) {
4829             cfg.cls += ' table-hover';
4830         }
4831         if (this.bordered) {
4832             cfg.cls += ' table-bordered';
4833         }
4834         if (this.condensed) {
4835             cfg.cls += ' table-condensed';
4836         }
4837         if (this.responsive) {
4838             cfg.cls += ' table-responsive';
4839         }
4840         
4841         if (this.cls) {
4842             cfg.cls+=  ' ' +this.cls;
4843         }
4844         
4845         // this lot should be simplifed...
4846         
4847         if (this.align) {
4848             cfg.align=this.align;
4849         }
4850         if (this.bgcolor) {
4851             cfg.bgcolor=this.bgcolor;
4852         }
4853         if (this.border) {
4854             cfg.border=this.border;
4855         }
4856         if (this.cellpadding) {
4857             cfg.cellpadding=this.cellpadding;
4858         }
4859         if (this.cellspacing) {
4860             cfg.cellspacing=this.cellspacing;
4861         }
4862         if (this.frame) {
4863             cfg.frame=this.frame;
4864         }
4865         if (this.rules) {
4866             cfg.rules=this.rules;
4867         }
4868         if (this.sortable) {
4869             cfg.sortable=this.sortable;
4870         }
4871         if (this.summary) {
4872             cfg.summary=this.summary;
4873         }
4874         if (this.width) {
4875             cfg.width=this.width;
4876         }
4877         if (this.layout) {
4878             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
4879         }
4880         
4881         if(this.store || this.cm){
4882             if(this.thead){
4883                 cfg.cn.push(this.renderHeader());
4884             }
4885             
4886             cfg.cn.push(this.renderBody());
4887             
4888             if(this.tfoot){
4889                 cfg.cn.push(this.renderFooter());
4890             }
4891             
4892             cfg.cls+=  ' TableGrid';
4893         }
4894         
4895         return { cn : [ cfg ] };
4896     },
4897     
4898     initEvents : function()
4899     {   
4900         if(!this.store || !this.cm){
4901             return;
4902         }
4903         
4904         //Roo.log('initEvents with ds!!!!');
4905         
4906         this.mainBody = this.el.select('tbody', true).first();
4907         
4908         
4909         var _this = this;
4910         
4911         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
4912             e.on('click', _this.sort, _this);
4913         });
4914         
4915         this.el.on("click", this.onClick, this);
4916         this.el.on("dblclick", this.onDblClick, this);
4917         
4918         this.parent().el.setStyle('position', 'relative');
4919         if (this.footer) {
4920             this.footer.parentId = this.id;
4921             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
4922         }
4923         
4924         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
4925         
4926         this.store.on('load', this.onLoad, this);
4927         this.store.on('beforeload', this.onBeforeLoad, this);
4928         this.store.on('update', this.onUpdate, this);
4929         
4930     },
4931     
4932     onMouseover : function(e, el)
4933     {
4934         var cell = Roo.get(el);
4935         
4936         if(!cell){
4937             return;
4938         }
4939         
4940         if(e.getTarget().nodeName.toLowerCase() != 'td'){
4941             cell = cell.findParent('td', false, true);
4942         }
4943         
4944         var row = cell.findParent('tr', false, true);
4945         var cellIndex = cell.dom.cellIndex;
4946         var rowIndex = row.dom.rowIndex - 1; // start from 0
4947         
4948         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
4949         
4950     },
4951     
4952     onMouseout : function(e, el)
4953     {
4954         var cell = Roo.get(el);
4955         
4956         if(!cell){
4957             return;
4958         }
4959         
4960         if(e.getTarget().nodeName.toLowerCase() != 'td'){
4961             cell = cell.findParent('td', false, true);
4962         }
4963         
4964         var row = cell.findParent('tr', false, true);
4965         var cellIndex = cell.dom.cellIndex;
4966         var rowIndex = row.dom.rowIndex - 1; // start from 0
4967         
4968         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
4969         
4970     },
4971     
4972     onClick : function(e, el)
4973     {
4974         var cell = Roo.get(el);
4975         
4976         if(!cell || (!this.CellSelection && !this.RowSelection)){
4977             return;
4978         }
4979         
4980         
4981         if(e.getTarget().nodeName.toLowerCase() != 'td'){
4982             cell = cell.findParent('td', false, true);
4983         }
4984         
4985         var row = cell.findParent('tr', false, true);
4986         var cellIndex = cell.dom.cellIndex;
4987         var rowIndex = row.dom.rowIndex - 1;
4988         
4989         if(this.CellSelection){
4990             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
4991         }
4992         
4993         if(this.RowSelection){
4994             this.fireEvent('rowclick', this, row, rowIndex, e);
4995         }
4996         
4997         
4998     },
4999     
5000     onDblClick : function(e,el)
5001     {
5002         var cell = Roo.get(el);
5003         
5004         if(!cell || (!this.CellSelection && !this.RowSelection)){
5005             return;
5006         }
5007         
5008         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5009             cell = cell.findParent('td', false, true);
5010         }
5011         
5012         var row = cell.findParent('tr', false, true);
5013         var cellIndex = cell.dom.cellIndex;
5014         var rowIndex = row.dom.rowIndex - 1;
5015         
5016         if(this.CellSelection){
5017             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5018         }
5019         
5020         if(this.RowSelection){
5021             this.fireEvent('rowdblclick', this, row, rowIndex, e);
5022         }
5023     },
5024     
5025     sort : function(e,el)
5026     {
5027         var col = Roo.get(el)
5028         
5029         if(!col.hasClass('sortable')){
5030             return;
5031         }
5032         
5033         var sort = col.attr('sort');
5034         var dir = 'ASC';
5035         
5036         if(col.hasClass('glyphicon-arrow-up')){
5037             dir = 'DESC';
5038         }
5039         
5040         this.store.sortInfo = {field : sort, direction : dir};
5041         
5042         if (this.footer) {
5043             Roo.log("calling footer first");
5044             this.footer.onClick('first');
5045         } else {
5046         
5047             this.store.load({ params : { start : 0 } });
5048         }
5049     },
5050     
5051     renderHeader : function()
5052     {
5053         var header = {
5054             tag: 'thead',
5055             cn : []
5056         };
5057         
5058         var cm = this.cm;
5059         
5060         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5061             
5062             var config = cm.config[i];
5063                     
5064             var c = {
5065                 tag: 'th',
5066                 style : '',
5067                 html: cm.getColumnHeader(i)
5068             };
5069             
5070             if(typeof(config.hidden) != 'undefined' && config.hidden){
5071                 c.style += ' display:none;';
5072             }
5073             
5074             if(typeof(config.dataIndex) != 'undefined'){
5075                 c.sort = config.dataIndex;
5076             }
5077             
5078             if(typeof(config.sortable) != 'undefined' && config.sortable){
5079                 c.cls = 'sortable';
5080             }
5081             
5082             if(typeof(config.align) != 'undefined' && config.align.length){
5083                 c.style += ' text-align:' + config.align + ';';
5084             }
5085             
5086             if(typeof(config.width) != 'undefined'){
5087                 c.style += ' width:' + config.width + 'px;';
5088             }
5089             
5090             header.cn.push(c)
5091         }
5092         
5093         return header;
5094     },
5095     
5096     renderBody : function()
5097     {
5098         var body = {
5099             tag: 'tbody',
5100             cn : [
5101                 {
5102                     tag: 'tr',
5103                     cn : [
5104                         {
5105                             tag : 'td',
5106                             colspan :  this.cm.getColumnCount()
5107                         }
5108                     ]
5109                 }
5110             ]
5111         };
5112         
5113         return body;
5114     },
5115     
5116     renderFooter : function()
5117     {
5118         var footer = {
5119             tag: 'tfoot',
5120             cn : [
5121                 {
5122                     tag: 'tr',
5123                     cn : [
5124                         {
5125                             tag : 'td',
5126                             colspan :  this.cm.getColumnCount()
5127                         }
5128                     ]
5129                 }
5130             ]
5131         };
5132         
5133         return footer;
5134     },
5135     
5136     
5137     
5138     onLoad : function()
5139     {
5140         Roo.log('ds onload');
5141         this.clear();
5142         
5143         var _this = this;
5144         var cm = this.cm;
5145         var ds = this.store;
5146         
5147         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5148             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
5149             
5150             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
5151                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
5152             }
5153             
5154             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
5155                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
5156             }
5157         });
5158         
5159         var tbody =  this.mainBody;
5160         
5161         var renders = [];
5162                     
5163         if(ds.getCount() > 0){
5164             ds.data.each(function(d,rowIndex){
5165                 var row =  this.renderRow(cm, ds, rowIndex);
5166                 
5167                 tbody.createChild(row);
5168                 
5169                 var _this = this;
5170                 
5171                 if(row.cellObjects.length){
5172                     Roo.each(row.cellObjects, function(r){
5173                         _this.renderCellObject(r);
5174                     })
5175                 }
5176                 
5177             }, this);
5178         }
5179         
5180         Roo.each(this.el.select('tbody td', true).elements, function(e){
5181             e.on('mouseover', _this.onMouseover, _this);
5182         });
5183         
5184         Roo.each(this.el.select('tbody td', true).elements, function(e){
5185             e.on('mouseout', _this.onMouseout, _this);
5186         });
5187
5188         //if(this.loadMask){
5189         //    this.maskEl.hide();
5190         //}
5191     },
5192     
5193     
5194     onUpdate : function(ds,record)
5195     {
5196         this.refreshRow(record);
5197     },
5198     onRemove : function(ds, record, index, isUpdate){
5199         if(isUpdate !== true){
5200             this.fireEvent("beforerowremoved", this, index, record);
5201         }
5202         var bt = this.mainBody.dom;
5203         if(bt.rows[index]){
5204             bt.removeChild(bt.rows[index]);
5205         }
5206         
5207         if(isUpdate !== true){
5208             //this.stripeRows(index);
5209             //this.syncRowHeights(index, index);
5210             //this.layout();
5211             this.fireEvent("rowremoved", this, index, record);
5212         }
5213     },
5214     
5215     
5216     refreshRow : function(record){
5217         var ds = this.store, index;
5218         if(typeof record == 'number'){
5219             index = record;
5220             record = ds.getAt(index);
5221         }else{
5222             index = ds.indexOf(record);
5223         }
5224         this.insertRow(ds, index, true);
5225         this.onRemove(ds, record, index+1, true);
5226         //this.syncRowHeights(index, index);
5227         //this.layout();
5228         this.fireEvent("rowupdated", this, index, record);
5229     },
5230     
5231     insertRow : function(dm, rowIndex, isUpdate){
5232         
5233         if(!isUpdate){
5234             this.fireEvent("beforerowsinserted", this, rowIndex);
5235         }
5236             //var s = this.getScrollState();
5237         var row = this.renderRow(this.cm, this.store, rowIndex);
5238         // insert before rowIndex..
5239         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
5240         Roo.log(e);
5241         
5242         var _this = this;
5243                 
5244         if(row.cellObjects.length){
5245             Roo.each(row.cellObjects, function(r){
5246                 _this.renderCellObject(r);
5247             })
5248         }
5249             
5250         if(!isUpdate){
5251             this.fireEvent("rowsinserted", this, rowIndex);
5252             //this.syncRowHeights(firstRow, lastRow);
5253             //this.stripeRows(firstRow);
5254             //this.layout();
5255         }
5256         
5257     },
5258     
5259     
5260     getRowDom : function(rowIndex)
5261     {
5262         // not sure if I need to check this.. but let's do it anyway..
5263         return (this.mainBody.dom.rows && (rowIndex-1) < this.mainBody.dom.rows.length ) ?
5264                 this.mainBody.dom.rows[rowIndex] : false
5265     },
5266     // returns the object tree for a tr..
5267   
5268     
5269     renderRow : function(cm, ds, rowIndex) {
5270         
5271         var d = ds.getAt(rowIndex);
5272         
5273         var row = {
5274             tag : 'tr',
5275             cn : []
5276         };
5277             
5278         var cellObjects = [];
5279         
5280         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5281             var config = cm.config[i];
5282             
5283             var renderer = cm.getRenderer(i);
5284             var value = '';
5285             var id = false;
5286             
5287             if(typeof(renderer) !== 'undefined'){
5288                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
5289             }
5290             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
5291             // and are rendered into the cells after the row is rendered - using the id for the element.
5292             
5293             if(typeof(value) === 'object'){
5294                 id = Roo.id();
5295                 cellObjects.push({
5296                     container : id,
5297                     cfg : value 
5298                 })
5299             }
5300             
5301             var rowcfg = {
5302                 record: d,
5303                 rowIndex : rowIndex,
5304                 colIndex : i,
5305                 rowClass : ''
5306             }
5307
5308             this.fireEvent('rowclass', this, rowcfg);
5309             
5310             var td = {
5311                 tag: 'td',
5312                 cls : rowcfg.rowClass,
5313                 style: '',
5314                 html: (typeof(value) === 'object') ? '' : value
5315             };
5316             
5317             if (id) {
5318                 td.id = id;
5319             }
5320             
5321             if(typeof(config.hidden) != 'undefined' && config.hidden){
5322                 td.style += ' display:none;';
5323             }
5324             
5325             if(typeof(config.align) != 'undefined' && config.align.length){
5326                 td.style += ' text-align:' + config.align + ';';
5327             }
5328             
5329             if(typeof(config.width) != 'undefined'){
5330                 td.style += ' width:' +  config.width + 'px;';
5331             }
5332              
5333             row.cn.push(td);
5334            
5335         }
5336         
5337         row.cellObjects = cellObjects;
5338         
5339         return row;
5340           
5341     },
5342     
5343     
5344     
5345     onBeforeLoad : function()
5346     {
5347         //Roo.log('ds onBeforeLoad');
5348         
5349         //this.clear();
5350         
5351         //if(this.loadMask){
5352         //    this.maskEl.show();
5353         //}
5354     },
5355     
5356     clear : function()
5357     {
5358         this.el.select('tbody', true).first().dom.innerHTML = '';
5359     },
5360     
5361     getSelectionModel : function(){
5362         if(!this.selModel){
5363             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
5364         }
5365         return this.selModel;
5366     },
5367     /*
5368      * Render the Roo.bootstrap object from renderder
5369      */
5370     renderCellObject : function(r)
5371     {
5372         var _this = this;
5373         
5374         var t = r.cfg.render(r.container);
5375         
5376         if(r.cfg.cn){
5377             Roo.each(r.cfg.cn, function(c){
5378                 var child = {
5379                     container: t.getChildContainer(),
5380                     cfg: c
5381                 }
5382                 _this.renderCellObject(child);
5383             })
5384         }
5385     }
5386    
5387 });
5388
5389  
5390
5391  /*
5392  * - LGPL
5393  *
5394  * table cell
5395  * 
5396  */
5397
5398 /**
5399  * @class Roo.bootstrap.TableCell
5400  * @extends Roo.bootstrap.Component
5401  * Bootstrap TableCell class
5402  * @cfg {String} html cell contain text
5403  * @cfg {String} cls cell class
5404  * @cfg {String} tag cell tag (td|th) default td
5405  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
5406  * @cfg {String} align Aligns the content in a cell
5407  * @cfg {String} axis Categorizes cells
5408  * @cfg {String} bgcolor Specifies the background color of a cell
5409  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5410  * @cfg {Number} colspan Specifies the number of columns a cell should span
5411  * @cfg {String} headers Specifies one or more header cells a cell is related to
5412  * @cfg {Number} height Sets the height of a cell
5413  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
5414  * @cfg {Number} rowspan Sets the number of rows a cell should span
5415  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
5416  * @cfg {String} valign Vertical aligns the content in a cell
5417  * @cfg {Number} width Specifies the width of a cell
5418  * 
5419  * @constructor
5420  * Create a new TableCell
5421  * @param {Object} config The config object
5422  */
5423
5424 Roo.bootstrap.TableCell = function(config){
5425     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
5426 };
5427
5428 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
5429     
5430     html: false,
5431     cls: false,
5432     tag: false,
5433     abbr: false,
5434     align: false,
5435     axis: false,
5436     bgcolor: false,
5437     charoff: false,
5438     colspan: false,
5439     headers: false,
5440     height: false,
5441     nowrap: false,
5442     rowspan: false,
5443     scope: false,
5444     valign: false,
5445     width: false,
5446     
5447     
5448     getAutoCreate : function(){
5449         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
5450         
5451         cfg = {
5452             tag: 'td'
5453         }
5454         
5455         if(this.tag){
5456             cfg.tag = this.tag;
5457         }
5458         
5459         if (this.html) {
5460             cfg.html=this.html
5461         }
5462         if (this.cls) {
5463             cfg.cls=this.cls
5464         }
5465         if (this.abbr) {
5466             cfg.abbr=this.abbr
5467         }
5468         if (this.align) {
5469             cfg.align=this.align
5470         }
5471         if (this.axis) {
5472             cfg.axis=this.axis
5473         }
5474         if (this.bgcolor) {
5475             cfg.bgcolor=this.bgcolor
5476         }
5477         if (this.charoff) {
5478             cfg.charoff=this.charoff
5479         }
5480         if (this.colspan) {
5481             cfg.colspan=this.colspan
5482         }
5483         if (this.headers) {
5484             cfg.headers=this.headers
5485         }
5486         if (this.height) {
5487             cfg.height=this.height
5488         }
5489         if (this.nowrap) {
5490             cfg.nowrap=this.nowrap
5491         }
5492         if (this.rowspan) {
5493             cfg.rowspan=this.rowspan
5494         }
5495         if (this.scope) {
5496             cfg.scope=this.scope
5497         }
5498         if (this.valign) {
5499             cfg.valign=this.valign
5500         }
5501         if (this.width) {
5502             cfg.width=this.width
5503         }
5504         
5505         
5506         return cfg;
5507     }
5508    
5509 });
5510
5511  
5512
5513  /*
5514  * - LGPL
5515  *
5516  * table row
5517  * 
5518  */
5519
5520 /**
5521  * @class Roo.bootstrap.TableRow
5522  * @extends Roo.bootstrap.Component
5523  * Bootstrap TableRow class
5524  * @cfg {String} cls row class
5525  * @cfg {String} align Aligns the content in a table row
5526  * @cfg {String} bgcolor Specifies a background color for a table row
5527  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5528  * @cfg {String} valign Vertical aligns the content in a table row
5529  * 
5530  * @constructor
5531  * Create a new TableRow
5532  * @param {Object} config The config object
5533  */
5534
5535 Roo.bootstrap.TableRow = function(config){
5536     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
5537 };
5538
5539 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
5540     
5541     cls: false,
5542     align: false,
5543     bgcolor: false,
5544     charoff: false,
5545     valign: false,
5546     
5547     getAutoCreate : function(){
5548         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
5549         
5550         cfg = {
5551             tag: 'tr'
5552         }
5553             
5554         if(this.cls){
5555             cfg.cls = this.cls;
5556         }
5557         if(this.align){
5558             cfg.align = this.align;
5559         }
5560         if(this.bgcolor){
5561             cfg.bgcolor = this.bgcolor;
5562         }
5563         if(this.charoff){
5564             cfg.charoff = this.charoff;
5565         }
5566         if(this.valign){
5567             cfg.valign = this.valign;
5568         }
5569         
5570         return cfg;
5571     }
5572    
5573 });
5574
5575  
5576
5577  /*
5578  * - LGPL
5579  *
5580  * table body
5581  * 
5582  */
5583
5584 /**
5585  * @class Roo.bootstrap.TableBody
5586  * @extends Roo.bootstrap.Component
5587  * Bootstrap TableBody class
5588  * @cfg {String} cls element class
5589  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
5590  * @cfg {String} align Aligns the content inside the element
5591  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
5592  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
5593  * 
5594  * @constructor
5595  * Create a new TableBody
5596  * @param {Object} config The config object
5597  */
5598
5599 Roo.bootstrap.TableBody = function(config){
5600     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
5601 };
5602
5603 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
5604     
5605     cls: false,
5606     tag: false,
5607     align: false,
5608     charoff: false,
5609     valign: false,
5610     
5611     getAutoCreate : function(){
5612         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
5613         
5614         cfg = {
5615             tag: 'tbody'
5616         }
5617             
5618         if (this.cls) {
5619             cfg.cls=this.cls
5620         }
5621         if(this.tag){
5622             cfg.tag = this.tag;
5623         }
5624         
5625         if(this.align){
5626             cfg.align = this.align;
5627         }
5628         if(this.charoff){
5629             cfg.charoff = this.charoff;
5630         }
5631         if(this.valign){
5632             cfg.valign = this.valign;
5633         }
5634         
5635         return cfg;
5636     }
5637     
5638     
5639 //    initEvents : function()
5640 //    {
5641 //        
5642 //        if(!this.store){
5643 //            return;
5644 //        }
5645 //        
5646 //        this.store = Roo.factory(this.store, Roo.data);
5647 //        this.store.on('load', this.onLoad, this);
5648 //        
5649 //        this.store.load();
5650 //        
5651 //    },
5652 //    
5653 //    onLoad: function () 
5654 //    {   
5655 //        this.fireEvent('load', this);
5656 //    }
5657 //    
5658 //   
5659 });
5660
5661  
5662
5663  /*
5664  * Based on:
5665  * Ext JS Library 1.1.1
5666  * Copyright(c) 2006-2007, Ext JS, LLC.
5667  *
5668  * Originally Released Under LGPL - original licence link has changed is not relivant.
5669  *
5670  * Fork - LGPL
5671  * <script type="text/javascript">
5672  */
5673
5674 // as we use this in bootstrap.
5675 Roo.namespace('Roo.form');
5676  /**
5677  * @class Roo.form.Action
5678  * Internal Class used to handle form actions
5679  * @constructor
5680  * @param {Roo.form.BasicForm} el The form element or its id
5681  * @param {Object} config Configuration options
5682  */
5683
5684  
5685  
5686 // define the action interface
5687 Roo.form.Action = function(form, options){
5688     this.form = form;
5689     this.options = options || {};
5690 };
5691 /**
5692  * Client Validation Failed
5693  * @const 
5694  */
5695 Roo.form.Action.CLIENT_INVALID = 'client';
5696 /**
5697  * Server Validation Failed
5698  * @const 
5699  */
5700 Roo.form.Action.SERVER_INVALID = 'server';
5701  /**
5702  * Connect to Server Failed
5703  * @const 
5704  */
5705 Roo.form.Action.CONNECT_FAILURE = 'connect';
5706 /**
5707  * Reading Data from Server Failed
5708  * @const 
5709  */
5710 Roo.form.Action.LOAD_FAILURE = 'load';
5711
5712 Roo.form.Action.prototype = {
5713     type : 'default',
5714     failureType : undefined,
5715     response : undefined,
5716     result : undefined,
5717
5718     // interface method
5719     run : function(options){
5720
5721     },
5722
5723     // interface method
5724     success : function(response){
5725
5726     },
5727
5728     // interface method
5729     handleResponse : function(response){
5730
5731     },
5732
5733     // default connection failure
5734     failure : function(response){
5735         
5736         this.response = response;
5737         this.failureType = Roo.form.Action.CONNECT_FAILURE;
5738         this.form.afterAction(this, false);
5739     },
5740
5741     processResponse : function(response){
5742         this.response = response;
5743         if(!response.responseText){
5744             return true;
5745         }
5746         this.result = this.handleResponse(response);
5747         return this.result;
5748     },
5749
5750     // utility functions used internally
5751     getUrl : function(appendParams){
5752         var url = this.options.url || this.form.url || this.form.el.dom.action;
5753         if(appendParams){
5754             var p = this.getParams();
5755             if(p){
5756                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
5757             }
5758         }
5759         return url;
5760     },
5761
5762     getMethod : function(){
5763         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
5764     },
5765
5766     getParams : function(){
5767         var bp = this.form.baseParams;
5768         var p = this.options.params;
5769         if(p){
5770             if(typeof p == "object"){
5771                 p = Roo.urlEncode(Roo.applyIf(p, bp));
5772             }else if(typeof p == 'string' && bp){
5773                 p += '&' + Roo.urlEncode(bp);
5774             }
5775         }else if(bp){
5776             p = Roo.urlEncode(bp);
5777         }
5778         return p;
5779     },
5780
5781     createCallback : function(){
5782         return {
5783             success: this.success,
5784             failure: this.failure,
5785             scope: this,
5786             timeout: (this.form.timeout*1000),
5787             upload: this.form.fileUpload ? this.success : undefined
5788         };
5789     }
5790 };
5791
5792 Roo.form.Action.Submit = function(form, options){
5793     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
5794 };
5795
5796 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
5797     type : 'submit',
5798
5799     haveProgress : false,
5800     uploadComplete : false,
5801     
5802     // uploadProgress indicator.
5803     uploadProgress : function()
5804     {
5805         if (!this.form.progressUrl) {
5806             return;
5807         }
5808         
5809         if (!this.haveProgress) {
5810             Roo.MessageBox.progress("Uploading", "Uploading");
5811         }
5812         if (this.uploadComplete) {
5813            Roo.MessageBox.hide();
5814            return;
5815         }
5816         
5817         this.haveProgress = true;
5818    
5819         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
5820         
5821         var c = new Roo.data.Connection();
5822         c.request({
5823             url : this.form.progressUrl,
5824             params: {
5825                 id : uid
5826             },
5827             method: 'GET',
5828             success : function(req){
5829                //console.log(data);
5830                 var rdata = false;
5831                 var edata;
5832                 try  {
5833                    rdata = Roo.decode(req.responseText)
5834                 } catch (e) {
5835                     Roo.log("Invalid data from server..");
5836                     Roo.log(edata);
5837                     return;
5838                 }
5839                 if (!rdata || !rdata.success) {
5840                     Roo.log(rdata);
5841                     Roo.MessageBox.alert(Roo.encode(rdata));
5842                     return;
5843                 }
5844                 var data = rdata.data;
5845                 
5846                 if (this.uploadComplete) {
5847                    Roo.MessageBox.hide();
5848                    return;
5849                 }
5850                    
5851                 if (data){
5852                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
5853                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
5854                     );
5855                 }
5856                 this.uploadProgress.defer(2000,this);
5857             },
5858        
5859             failure: function(data) {
5860                 Roo.log('progress url failed ');
5861                 Roo.log(data);
5862             },
5863             scope : this
5864         });
5865            
5866     },
5867     
5868     
5869     run : function()
5870     {
5871         // run get Values on the form, so it syncs any secondary forms.
5872         this.form.getValues();
5873         
5874         var o = this.options;
5875         var method = this.getMethod();
5876         var isPost = method == 'POST';
5877         if(o.clientValidation === false || this.form.isValid()){
5878             
5879             if (this.form.progressUrl) {
5880                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
5881                     (new Date() * 1) + '' + Math.random());
5882                     
5883             } 
5884             
5885             
5886             Roo.Ajax.request(Roo.apply(this.createCallback(), {
5887                 form:this.form.el.dom,
5888                 url:this.getUrl(!isPost),
5889                 method: method,
5890                 params:isPost ? this.getParams() : null,
5891                 isUpload: this.form.fileUpload
5892             }));
5893             
5894             this.uploadProgress();
5895
5896         }else if (o.clientValidation !== false){ // client validation failed
5897             this.failureType = Roo.form.Action.CLIENT_INVALID;
5898             this.form.afterAction(this, false);
5899         }
5900     },
5901
5902     success : function(response)
5903     {
5904         this.uploadComplete= true;
5905         if (this.haveProgress) {
5906             Roo.MessageBox.hide();
5907         }
5908         
5909         
5910         var result = this.processResponse(response);
5911         if(result === true || result.success){
5912             this.form.afterAction(this, true);
5913             return;
5914         }
5915         if(result.errors){
5916             this.form.markInvalid(result.errors);
5917             this.failureType = Roo.form.Action.SERVER_INVALID;
5918         }
5919         this.form.afterAction(this, false);
5920     },
5921     failure : function(response)
5922     {
5923         this.uploadComplete= true;
5924         if (this.haveProgress) {
5925             Roo.MessageBox.hide();
5926         }
5927         
5928         this.response = response;
5929         this.failureType = Roo.form.Action.CONNECT_FAILURE;
5930         this.form.afterAction(this, false);
5931     },
5932     
5933     handleResponse : function(response){
5934         if(this.form.errorReader){
5935             var rs = this.form.errorReader.read(response);
5936             var errors = [];
5937             if(rs.records){
5938                 for(var i = 0, len = rs.records.length; i < len; i++) {
5939                     var r = rs.records[i];
5940                     errors[i] = r.data;
5941                 }
5942             }
5943             if(errors.length < 1){
5944                 errors = null;
5945             }
5946             return {
5947                 success : rs.success,
5948                 errors : errors
5949             };
5950         }
5951         var ret = false;
5952         try {
5953             ret = Roo.decode(response.responseText);
5954         } catch (e) {
5955             ret = {
5956                 success: false,
5957                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
5958                 errors : []
5959             };
5960         }
5961         return ret;
5962         
5963     }
5964 });
5965
5966
5967 Roo.form.Action.Load = function(form, options){
5968     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
5969     this.reader = this.form.reader;
5970 };
5971
5972 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
5973     type : 'load',
5974
5975     run : function(){
5976         
5977         Roo.Ajax.request(Roo.apply(
5978                 this.createCallback(), {
5979                     method:this.getMethod(),
5980                     url:this.getUrl(false),
5981                     params:this.getParams()
5982         }));
5983     },
5984
5985     success : function(response){
5986         
5987         var result = this.processResponse(response);
5988         if(result === true || !result.success || !result.data){
5989             this.failureType = Roo.form.Action.LOAD_FAILURE;
5990             this.form.afterAction(this, false);
5991             return;
5992         }
5993         this.form.clearInvalid();
5994         this.form.setValues(result.data);
5995         this.form.afterAction(this, true);
5996     },
5997
5998     handleResponse : function(response){
5999         if(this.form.reader){
6000             var rs = this.form.reader.read(response);
6001             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
6002             return {
6003                 success : rs.success,
6004                 data : data
6005             };
6006         }
6007         return Roo.decode(response.responseText);
6008     }
6009 });
6010
6011 Roo.form.Action.ACTION_TYPES = {
6012     'load' : Roo.form.Action.Load,
6013     'submit' : Roo.form.Action.Submit
6014 };/*
6015  * - LGPL
6016  *
6017  * form
6018  * 
6019  */
6020
6021 /**
6022  * @class Roo.bootstrap.Form
6023  * @extends Roo.bootstrap.Component
6024  * Bootstrap Form class
6025  * @cfg {String} method  GET | POST (default POST)
6026  * @cfg {String} labelAlign top | left (default top)
6027   * @cfg {String} align left  | right - for navbars
6028
6029  * 
6030  * @constructor
6031  * Create a new Form
6032  * @param {Object} config The config object
6033  */
6034
6035
6036 Roo.bootstrap.Form = function(config){
6037     Roo.bootstrap.Form.superclass.constructor.call(this, config);
6038     this.addEvents({
6039         /**
6040          * @event clientvalidation
6041          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
6042          * @param {Form} this
6043          * @param {Boolean} valid true if the form has passed client-side validation
6044          */
6045         clientvalidation: true,
6046         /**
6047          * @event beforeaction
6048          * Fires before any action is performed. Return false to cancel the action.
6049          * @param {Form} this
6050          * @param {Action} action The action to be performed
6051          */
6052         beforeaction: true,
6053         /**
6054          * @event actionfailed
6055          * Fires when an action fails.
6056          * @param {Form} this
6057          * @param {Action} action The action that failed
6058          */
6059         actionfailed : true,
6060         /**
6061          * @event actioncomplete
6062          * Fires when an action is completed.
6063          * @param {Form} this
6064          * @param {Action} action The action that completed
6065          */
6066         actioncomplete : true
6067     });
6068     
6069 };
6070
6071 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
6072       
6073      /**
6074      * @cfg {String} method
6075      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
6076      */
6077     method : 'POST',
6078     /**
6079      * @cfg {String} url
6080      * The URL to use for form actions if one isn't supplied in the action options.
6081      */
6082     /**
6083      * @cfg {Boolean} fileUpload
6084      * Set to true if this form is a file upload.
6085      */
6086      
6087     /**
6088      * @cfg {Object} baseParams
6089      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
6090      */
6091       
6092     /**
6093      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
6094      */
6095     timeout: 30,
6096     /**
6097      * @cfg {Sting} align (left|right) for navbar forms
6098      */
6099     align : 'left',
6100
6101     // private
6102     activeAction : null,
6103  
6104     /**
6105      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6106      * element by passing it or its id or mask the form itself by passing in true.
6107      * @type Mixed
6108      */
6109     waitMsgTarget : false,
6110     
6111      
6112     
6113     /**
6114      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6115      * element by passing it or its id or mask the form itself by passing in true.
6116      * @type Mixed
6117      */
6118     
6119     getAutoCreate : function(){
6120         
6121         var cfg = {
6122             tag: 'form',
6123             method : this.method || 'POST',
6124             id : this.id || Roo.id(),
6125             cls : ''
6126         }
6127         if (this.parent().xtype.match(/^Nav/)) {
6128             cfg.cls = 'navbar-form navbar-' + this.align;
6129             
6130         }
6131         
6132         if (this.labelAlign == 'left' ) {
6133             cfg.cls += ' form-horizontal';
6134         }
6135         
6136         
6137         return cfg;
6138     },
6139     initEvents : function()
6140     {
6141         this.el.on('submit', this.onSubmit, this);
6142         // this was added as random key presses on the form where triggering form submit.
6143         this.el.on('keypress', function(e) {
6144             if (e.getCharCode() != 13) {
6145                 return true;
6146             }
6147             // we might need to allow it for textareas.. and some other items.
6148             // check e.getTarget().
6149             
6150             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
6151                 return true;
6152             }
6153         
6154             Roo.log("keypress blocked");
6155             
6156             e.preventDefault();
6157             return false;
6158         });
6159         
6160     },
6161     // private
6162     onSubmit : function(e){
6163         e.stopEvent();
6164     },
6165     
6166      /**
6167      * Returns true if client-side validation on the form is successful.
6168      * @return Boolean
6169      */
6170     isValid : function(){
6171         var items = this.getItems();
6172         var valid = true;
6173         items.each(function(f){
6174            if(!f.validate()){
6175                valid = false;
6176                
6177            }
6178         });
6179         return valid;
6180     },
6181     /**
6182      * Returns true if any fields in this form have changed since their original load.
6183      * @return Boolean
6184      */
6185     isDirty : function(){
6186         var dirty = false;
6187         var items = this.getItems();
6188         items.each(function(f){
6189            if(f.isDirty()){
6190                dirty = true;
6191                return false;
6192            }
6193            return true;
6194         });
6195         return dirty;
6196     },
6197      /**
6198      * Performs a predefined action (submit or load) or custom actions you define on this form.
6199      * @param {String} actionName The name of the action type
6200      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
6201      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
6202      * accept other config options):
6203      * <pre>
6204 Property          Type             Description
6205 ----------------  ---------------  ----------------------------------------------------------------------------------
6206 url               String           The url for the action (defaults to the form's url)
6207 method            String           The form method to use (defaults to the form's method, or POST if not defined)
6208 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
6209 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
6210                                    validate the form on the client (defaults to false)
6211      * </pre>
6212      * @return {BasicForm} this
6213      */
6214     doAction : function(action, options){
6215         if(typeof action == 'string'){
6216             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
6217         }
6218         if(this.fireEvent('beforeaction', this, action) !== false){
6219             this.beforeAction(action);
6220             action.run.defer(100, action);
6221         }
6222         return this;
6223     },
6224     
6225     // private
6226     beforeAction : function(action){
6227         var o = action.options;
6228         
6229         // not really supported yet.. ??
6230         
6231         //if(this.waitMsgTarget === true){
6232             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6233         //}else if(this.waitMsgTarget){
6234         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
6235         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
6236         //}else {
6237         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
6238        // }
6239          
6240     },
6241
6242     // private
6243     afterAction : function(action, success){
6244         this.activeAction = null;
6245         var o = action.options;
6246         
6247         //if(this.waitMsgTarget === true){
6248             this.el.unmask();
6249         //}else if(this.waitMsgTarget){
6250         //    this.waitMsgTarget.unmask();
6251         //}else{
6252         //    Roo.MessageBox.updateProgress(1);
6253         //    Roo.MessageBox.hide();
6254        // }
6255         // 
6256         if(success){
6257             if(o.reset){
6258                 this.reset();
6259             }
6260             Roo.callback(o.success, o.scope, [this, action]);
6261             this.fireEvent('actioncomplete', this, action);
6262             
6263         }else{
6264             
6265             // failure condition..
6266             // we have a scenario where updates need confirming.
6267             // eg. if a locking scenario exists..
6268             // we look for { errors : { needs_confirm : true }} in the response.
6269             if (
6270                 (typeof(action.result) != 'undefined')  &&
6271                 (typeof(action.result.errors) != 'undefined')  &&
6272                 (typeof(action.result.errors.needs_confirm) != 'undefined')
6273            ){
6274                 var _t = this;
6275                 Roo.log("not supported yet");
6276                  /*
6277                 
6278                 Roo.MessageBox.confirm(
6279                     "Change requires confirmation",
6280                     action.result.errorMsg,
6281                     function(r) {
6282                         if (r != 'yes') {
6283                             return;
6284                         }
6285                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
6286                     }
6287                     
6288                 );
6289                 */
6290                 
6291                 
6292                 return;
6293             }
6294             
6295             Roo.callback(o.failure, o.scope, [this, action]);
6296             // show an error message if no failed handler is set..
6297             if (!this.hasListener('actionfailed')) {
6298                 Roo.log("need to add dialog support");
6299                 /*
6300                 Roo.MessageBox.alert("Error",
6301                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
6302                         action.result.errorMsg :
6303                         "Saving Failed, please check your entries or try again"
6304                 );
6305                 */
6306             }
6307             
6308             this.fireEvent('actionfailed', this, action);
6309         }
6310         
6311     },
6312     /**
6313      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
6314      * @param {String} id The value to search for
6315      * @return Field
6316      */
6317     findField : function(id){
6318         var items = this.getItems();
6319         var field = items.get(id);
6320         if(!field){
6321              items.each(function(f){
6322                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
6323                     field = f;
6324                     return false;
6325                 }
6326                 return true;
6327             });
6328         }
6329         return field || null;
6330     },
6331      /**
6332      * Mark fields in this form invalid in bulk.
6333      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
6334      * @return {BasicForm} this
6335      */
6336     markInvalid : function(errors){
6337         if(errors instanceof Array){
6338             for(var i = 0, len = errors.length; i < len; i++){
6339                 var fieldError = errors[i];
6340                 var f = this.findField(fieldError.id);
6341                 if(f){
6342                     f.markInvalid(fieldError.msg);
6343                 }
6344             }
6345         }else{
6346             var field, id;
6347             for(id in errors){
6348                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
6349                     field.markInvalid(errors[id]);
6350                 }
6351             }
6352         }
6353         //Roo.each(this.childForms || [], function (f) {
6354         //    f.markInvalid(errors);
6355         //});
6356         
6357         return this;
6358     },
6359
6360     /**
6361      * Set values for fields in this form in bulk.
6362      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
6363      * @return {BasicForm} this
6364      */
6365     setValues : function(values){
6366         if(values instanceof Array){ // array of objects
6367             for(var i = 0, len = values.length; i < len; i++){
6368                 var v = values[i];
6369                 var f = this.findField(v.id);
6370                 if(f){
6371                     f.setValue(v.value);
6372                     if(this.trackResetOnLoad){
6373                         f.originalValue = f.getValue();
6374                     }
6375                 }
6376             }
6377         }else{ // object hash
6378             var field, id;
6379             for(id in values){
6380                 if(typeof values[id] != 'function' && (field = this.findField(id))){
6381                     
6382                     if (field.setFromData && 
6383                         field.valueField && 
6384                         field.displayField &&
6385                         // combos' with local stores can 
6386                         // be queried via setValue()
6387                         // to set their value..
6388                         (field.store && !field.store.isLocal)
6389                         ) {
6390                         // it's a combo
6391                         var sd = { };
6392                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
6393                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
6394                         field.setFromData(sd);
6395                         
6396                     } else {
6397                         field.setValue(values[id]);
6398                     }
6399                     
6400                     
6401                     if(this.trackResetOnLoad){
6402                         field.originalValue = field.getValue();
6403                     }
6404                 }
6405             }
6406         }
6407          
6408         //Roo.each(this.childForms || [], function (f) {
6409         //    f.setValues(values);
6410         //});
6411                 
6412         return this;
6413     },
6414
6415     /**
6416      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
6417      * they are returned as an array.
6418      * @param {Boolean} asString
6419      * @return {Object}
6420      */
6421     getValues : function(asString){
6422         //if (this.childForms) {
6423             // copy values from the child forms
6424         //    Roo.each(this.childForms, function (f) {
6425         //        this.setValues(f.getValues());
6426         //    }, this);
6427         //}
6428         
6429         
6430         
6431         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
6432         if(asString === true){
6433             return fs;
6434         }
6435         return Roo.urlDecode(fs);
6436     },
6437     
6438     /**
6439      * Returns the fields in this form as an object with key/value pairs. 
6440      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
6441      * @return {Object}
6442      */
6443     getFieldValues : function(with_hidden)
6444     {
6445         var items = this.getItems();
6446         var ret = {};
6447         items.each(function(f){
6448             if (!f.getName()) {
6449                 return;
6450             }
6451             var v = f.getValue();
6452             if (f.inputType =='radio') {
6453                 if (typeof(ret[f.getName()]) == 'undefined') {
6454                     ret[f.getName()] = ''; // empty..
6455                 }
6456                 
6457                 if (!f.el.dom.checked) {
6458                     return;
6459                     
6460                 }
6461                 v = f.el.dom.value;
6462                 
6463             }
6464             
6465             // not sure if this supported any more..
6466             if ((typeof(v) == 'object') && f.getRawValue) {
6467                 v = f.getRawValue() ; // dates..
6468             }
6469             // combo boxes where name != hiddenName...
6470             if (f.name != f.getName()) {
6471                 ret[f.name] = f.getRawValue();
6472             }
6473             ret[f.getName()] = v;
6474         });
6475         
6476         return ret;
6477     },
6478
6479     /**
6480      * Clears all invalid messages in this form.
6481      * @return {BasicForm} this
6482      */
6483     clearInvalid : function(){
6484         var items = this.getItems();
6485         
6486         items.each(function(f){
6487            f.clearInvalid();
6488         });
6489         
6490         
6491         
6492         return this;
6493     },
6494
6495     /**
6496      * Resets this form.
6497      * @return {BasicForm} this
6498      */
6499     reset : function(){
6500         var items = this.getItems();
6501         items.each(function(f){
6502             f.reset();
6503         });
6504         
6505         Roo.each(this.childForms || [], function (f) {
6506             f.reset();
6507         });
6508        
6509         
6510         return this;
6511     },
6512     getItems : function()
6513     {
6514         var r=new Roo.util.MixedCollection(false, function(o){
6515             return o.id || (o.id = Roo.id());
6516         });
6517         var iter = function(el) {
6518             if (el.inputEl) {
6519                 r.add(el);
6520             }
6521             if (!el.items) {
6522                 return;
6523             }
6524             Roo.each(el.items,function(e) {
6525                 iter(e);
6526             });
6527             
6528             
6529         };
6530         iter(this);
6531         return r;
6532         
6533         
6534         
6535         
6536     }
6537     
6538 });
6539
6540  
6541 /*
6542  * Based on:
6543  * Ext JS Library 1.1.1
6544  * Copyright(c) 2006-2007, Ext JS, LLC.
6545  *
6546  * Originally Released Under LGPL - original licence link has changed is not relivant.
6547  *
6548  * Fork - LGPL
6549  * <script type="text/javascript">
6550  */
6551 /**
6552  * @class Roo.form.VTypes
6553  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
6554  * @singleton
6555  */
6556 Roo.form.VTypes = function(){
6557     // closure these in so they are only created once.
6558     var alpha = /^[a-zA-Z_]+$/;
6559     var alphanum = /^[a-zA-Z0-9_]+$/;
6560     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
6561     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
6562
6563     // All these messages and functions are configurable
6564     return {
6565         /**
6566          * The function used to validate email addresses
6567          * @param {String} value The email address
6568          */
6569         'email' : function(v){
6570             return email.test(v);
6571         },
6572         /**
6573          * The error text to display when the email validation function returns false
6574          * @type String
6575          */
6576         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
6577         /**
6578          * The keystroke filter mask to be applied on email input
6579          * @type RegExp
6580          */
6581         'emailMask' : /[a-z0-9_\.\-@]/i,
6582
6583         /**
6584          * The function used to validate URLs
6585          * @param {String} value The URL
6586          */
6587         'url' : function(v){
6588             return url.test(v);
6589         },
6590         /**
6591          * The error text to display when the url validation function returns false
6592          * @type String
6593          */
6594         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
6595         
6596         /**
6597          * The function used to validate alpha values
6598          * @param {String} value The value
6599          */
6600         'alpha' : function(v){
6601             return alpha.test(v);
6602         },
6603         /**
6604          * The error text to display when the alpha validation function returns false
6605          * @type String
6606          */
6607         'alphaText' : 'This field should only contain letters and _',
6608         /**
6609          * The keystroke filter mask to be applied on alpha input
6610          * @type RegExp
6611          */
6612         'alphaMask' : /[a-z_]/i,
6613
6614         /**
6615          * The function used to validate alphanumeric values
6616          * @param {String} value The value
6617          */
6618         'alphanum' : function(v){
6619             return alphanum.test(v);
6620         },
6621         /**
6622          * The error text to display when the alphanumeric validation function returns false
6623          * @type String
6624          */
6625         'alphanumText' : 'This field should only contain letters, numbers and _',
6626         /**
6627          * The keystroke filter mask to be applied on alphanumeric input
6628          * @type RegExp
6629          */
6630         'alphanumMask' : /[a-z0-9_]/i
6631     };
6632 }();/*
6633  * - LGPL
6634  *
6635  * Input
6636  * 
6637  */
6638
6639 /**
6640  * @class Roo.bootstrap.Input
6641  * @extends Roo.bootstrap.Component
6642  * Bootstrap Input class
6643  * @cfg {Boolean} disabled is it disabled
6644  * @cfg {String} fieldLabel - the label associated
6645  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
6646  * @cfg {String} name name of the input
6647  * @cfg {string} fieldLabel - the label associated
6648  * @cfg {string}  inputType - input / file submit ...
6649  * @cfg {string} placeholder - placeholder to put in text.
6650  * @cfg {string}  before - input group add on before
6651  * @cfg {string} after - input group add on after
6652  * @cfg {string} size - (lg|sm) or leave empty..
6653  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
6654  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
6655  * @cfg {Number} md colspan out of 12 for computer-sized screens
6656  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
6657  * @cfg {string} value default value of the input
6658  * @cfg {Number} labelWidth set the width of label (0-12)
6659  * @cfg {String} labelAlign (top|left)
6660  * @cfg {Boolean} readOnly Specifies that the field should be read-only
6661  * @cfg {String} align (left|center|right) Default left
6662  * 
6663  * 
6664  * @constructor
6665  * Create a new Input
6666  * @param {Object} config The config object
6667  */
6668
6669 Roo.bootstrap.Input = function(config){
6670     Roo.bootstrap.Input.superclass.constructor.call(this, config);
6671    
6672         this.addEvents({
6673             /**
6674              * @event focus
6675              * Fires when this field receives input focus.
6676              * @param {Roo.form.Field} this
6677              */
6678             focus : true,
6679             /**
6680              * @event blur
6681              * Fires when this field loses input focus.
6682              * @param {Roo.form.Field} this
6683              */
6684             blur : true,
6685             /**
6686              * @event specialkey
6687              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
6688              * {@link Roo.EventObject#getKey} to determine which key was pressed.
6689              * @param {Roo.form.Field} this
6690              * @param {Roo.EventObject} e The event object
6691              */
6692             specialkey : true,
6693             /**
6694              * @event change
6695              * Fires just before the field blurs if the field value has changed.
6696              * @param {Roo.form.Field} this
6697              * @param {Mixed} newValue The new value
6698              * @param {Mixed} oldValue The original value
6699              */
6700             change : true,
6701             /**
6702              * @event invalid
6703              * Fires after the field has been marked as invalid.
6704              * @param {Roo.form.Field} this
6705              * @param {String} msg The validation message
6706              */
6707             invalid : true,
6708             /**
6709              * @event valid
6710              * Fires after the field has been validated with no errors.
6711              * @param {Roo.form.Field} this
6712              */
6713             valid : true,
6714              /**
6715              * @event keyup
6716              * Fires after the key up
6717              * @param {Roo.form.Field} this
6718              * @param {Roo.EventObject}  e The event Object
6719              */
6720             keyup : true
6721         });
6722 };
6723
6724 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
6725      /**
6726      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
6727       automatic validation (defaults to "keyup").
6728      */
6729     validationEvent : "keyup",
6730      /**
6731      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
6732      */
6733     validateOnBlur : true,
6734     /**
6735      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
6736      */
6737     validationDelay : 250,
6738      /**
6739      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
6740      */
6741     focusClass : "x-form-focus",  // not needed???
6742     
6743        
6744     /**
6745      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
6746      */
6747     invalidClass : "has-error",
6748     
6749     /**
6750      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
6751      */
6752     selectOnFocus : false,
6753     
6754      /**
6755      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
6756      */
6757     maskRe : null,
6758        /**
6759      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
6760      */
6761     vtype : null,
6762     
6763       /**
6764      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
6765      */
6766     disableKeyFilter : false,
6767     
6768        /**
6769      * @cfg {Boolean} disabled True to disable the field (defaults to false).
6770      */
6771     disabled : false,
6772      /**
6773      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
6774      */
6775     allowBlank : true,
6776     /**
6777      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
6778      */
6779     blankText : "This field is required",
6780     
6781      /**
6782      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
6783      */
6784     minLength : 0,
6785     /**
6786      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
6787      */
6788     maxLength : Number.MAX_VALUE,
6789     /**
6790      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
6791      */
6792     minLengthText : "The minimum length for this field is {0}",
6793     /**
6794      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
6795      */
6796     maxLengthText : "The maximum length for this field is {0}",
6797   
6798     
6799     /**
6800      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
6801      * If available, this function will be called only after the basic validators all return true, and will be passed the
6802      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
6803      */
6804     validator : null,
6805     /**
6806      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
6807      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
6808      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
6809      */
6810     regex : null,
6811     /**
6812      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
6813      */
6814     regexText : "",
6815     
6816     
6817     
6818     fieldLabel : '',
6819     inputType : 'text',
6820     
6821     name : false,
6822     placeholder: false,
6823     before : false,
6824     after : false,
6825     size : false,
6826     // private
6827     hasFocus : false,
6828     preventMark: false,
6829     isFormField : true,
6830     value : '',
6831     labelWidth : 2,
6832     labelAlign : false,
6833     readOnly : false,
6834     align : false,
6835     formatedValue : false,
6836     
6837     parentLabelAlign : function()
6838     {
6839         var parent = this;
6840         while (parent.parent()) {
6841             parent = parent.parent();
6842             if (typeof(parent.labelAlign) !='undefined') {
6843                 return parent.labelAlign;
6844             }
6845         }
6846         return 'left';
6847         
6848     },
6849     
6850     getAutoCreate : function(){
6851         
6852         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
6853         
6854         var id = Roo.id();
6855         
6856         var cfg = {};
6857         
6858         if(this.inputType != 'hidden'){
6859             cfg.cls = 'form-group' //input-group
6860         }
6861         
6862         var input =  {
6863             tag: 'input',
6864             id : id,
6865             type : this.inputType,
6866             value : this.value,
6867             cls : 'form-control',
6868             placeholder : this.placeholder || ''
6869             
6870         };
6871         
6872         if(this.align){
6873             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
6874         }
6875         
6876         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
6877             input.maxLength = this.maxLength;
6878         }
6879         
6880         if (this.disabled) {
6881             input.disabled=true;
6882         }
6883         
6884         if (this.readOnly) {
6885             input.readonly=true;
6886         }
6887         
6888         if (this.name) {
6889             input.name = this.name;
6890         }
6891         if (this.size) {
6892             input.cls += ' input-' + this.size;
6893         }
6894         var settings=this;
6895         ['xs','sm','md','lg'].map(function(size){
6896             if (settings[size]) {
6897                 cfg.cls += ' col-' + size + '-' + settings[size];
6898             }
6899         });
6900         
6901         var inputblock = input;
6902         
6903         if (this.before || this.after) {
6904             
6905             inputblock = {
6906                 cls : 'input-group',
6907                 cn :  [] 
6908             };
6909             if (this.before && typeof(this.before) == 'string') {
6910                 
6911                 inputblock.cn.push({
6912                     tag :'span',
6913                     cls : 'roo-input-before input-group-addon',
6914                     html : this.before
6915                 });
6916             }
6917             if (this.before && typeof(this.before) == 'object') {
6918                 this.before = Roo.factory(this.before);
6919                 Roo.log(this.before);
6920                 inputblock.cn.push({
6921                     tag :'span',
6922                     cls : 'roo-input-before input-group-' +
6923                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
6924                 });
6925             }
6926             
6927             inputblock.cn.push(input);
6928             
6929             if (this.after && typeof(this.after) == 'string') {
6930                 inputblock.cn.push({
6931                     tag :'span',
6932                     cls : 'roo-input-after input-group-addon',
6933                     html : this.after
6934                 });
6935             }
6936             if (this.after && typeof(this.after) == 'object') {
6937                 this.after = Roo.factory(this.after);
6938                 Roo.log(this.after);
6939                 inputblock.cn.push({
6940                     tag :'span',
6941                     cls : 'roo-input-after input-group-' +
6942                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
6943                 });
6944             }
6945         };
6946         
6947         if (align ==='left' && this.fieldLabel.length) {
6948                 Roo.log("left and has label");
6949                 cfg.cn = [
6950                     
6951                     {
6952                         tag: 'label',
6953                         'for' :  id,
6954                         cls : 'control-label col-sm-' + this.labelWidth,
6955                         html : this.fieldLabel
6956                         
6957                     },
6958                     {
6959                         cls : "col-sm-" + (12 - this.labelWidth), 
6960                         cn: [
6961                             inputblock
6962                         ]
6963                     }
6964                     
6965                 ];
6966         } else if ( this.fieldLabel.length) {
6967                 Roo.log(" label");
6968                  cfg.cn = [
6969                    
6970                     {
6971                         tag: 'label',
6972                         //cls : 'input-group-addon',
6973                         html : this.fieldLabel
6974                         
6975                     },
6976                     
6977                     inputblock
6978                     
6979                 ];
6980
6981         } else {
6982             
6983                 Roo.log(" no label && no align");
6984                 cfg.cn = [
6985                     
6986                         inputblock
6987                     
6988                 ];
6989                 
6990                 
6991         };
6992         Roo.log('input-parentType: ' + this.parentType);
6993         
6994         if (this.parentType === 'Navbar' &&  this.parent().bar) {
6995            cfg.cls += ' navbar-form';
6996            Roo.log(cfg);
6997         }
6998         
6999         return cfg;
7000         
7001     },
7002     /**
7003      * return the real input element.
7004      */
7005     inputEl: function ()
7006     {
7007         return this.el.select('input.form-control',true).first();
7008     },
7009     setDisabled : function(v)
7010     {
7011         var i  = this.inputEl().dom;
7012         if (!v) {
7013             i.removeAttribute('disabled');
7014             return;
7015             
7016         }
7017         i.setAttribute('disabled','true');
7018     },
7019     initEvents : function()
7020     {
7021         
7022         this.inputEl().on("keydown" , this.fireKey,  this);
7023         this.inputEl().on("focus", this.onFocus,  this);
7024         this.inputEl().on("blur", this.onBlur,  this);
7025         
7026         this.inputEl().relayEvent('keyup', this);
7027
7028         // reference to original value for reset
7029         this.originalValue = this.getValue();
7030         //Roo.form.TextField.superclass.initEvents.call(this);
7031         if(this.validationEvent == 'keyup'){
7032             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
7033             this.inputEl().on('keyup', this.filterValidation, this);
7034         }
7035         else if(this.validationEvent !== false){
7036             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
7037         }
7038         
7039         if(this.selectOnFocus){
7040             this.on("focus", this.preFocus, this);
7041             
7042         }
7043         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
7044             this.inputEl().on("keypress", this.filterKeys, this);
7045         }
7046        /* if(this.grow){
7047             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
7048             this.el.on("click", this.autoSize,  this);
7049         }
7050         */
7051         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
7052             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
7053         }
7054         
7055         if (typeof(this.before) == 'object') {
7056             this.before.render(this.el.select('.roo-input-before',true).first());
7057         }
7058         if (typeof(this.after) == 'object') {
7059             this.after.render(this.el.select('.roo-input-after',true).first());
7060         }
7061         
7062         
7063     },
7064     filterValidation : function(e){
7065         if(!e.isNavKeyPress()){
7066             this.validationTask.delay(this.validationDelay);
7067         }
7068     },
7069      /**
7070      * Validates the field value
7071      * @return {Boolean} True if the value is valid, else false
7072      */
7073     validate : function(){
7074         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
7075         if(this.disabled || this.validateValue(this.getRawValue())){
7076             this.clearInvalid();
7077             return true;
7078         }
7079         return false;
7080     },
7081     
7082     
7083     /**
7084      * Validates a value according to the field's validation rules and marks the field as invalid
7085      * if the validation fails
7086      * @param {Mixed} value The value to validate
7087      * @return {Boolean} True if the value is valid, else false
7088      */
7089     validateValue : function(value){
7090         if(value.length < 1)  { // if it's blank
7091              if(this.allowBlank){
7092                 this.clearInvalid();
7093                 return true;
7094              }else{
7095                 this.markInvalid(this.blankText);
7096                 return false;
7097              }
7098         }
7099         if(value.length < this.minLength){
7100             this.markInvalid(String.format(this.minLengthText, this.minLength));
7101             return false;
7102         }
7103         if(value.length > this.maxLength){
7104             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
7105             return false;
7106         }
7107         if(this.vtype){
7108             var vt = Roo.form.VTypes;
7109             if(!vt[this.vtype](value, this)){
7110                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
7111                 return false;
7112             }
7113         }
7114         if(typeof this.validator == "function"){
7115             var msg = this.validator(value);
7116             if(msg !== true){
7117                 this.markInvalid(msg);
7118                 return false;
7119             }
7120         }
7121         if(this.regex && !this.regex.test(value)){
7122             this.markInvalid(this.regexText);
7123             return false;
7124         }
7125         return true;
7126     },
7127
7128     
7129     
7130      // private
7131     fireKey : function(e){
7132         //Roo.log('field ' + e.getKey());
7133         if(e.isNavKeyPress()){
7134             this.fireEvent("specialkey", this, e);
7135         }
7136     },
7137     focus : function (selectText){
7138         if(this.rendered){
7139             this.inputEl().focus();
7140             if(selectText === true){
7141                 this.inputEl().dom.select();
7142             }
7143         }
7144         return this;
7145     } ,
7146     
7147     onFocus : function(){
7148         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7149            // this.el.addClass(this.focusClass);
7150         }
7151         if(!this.hasFocus){
7152             this.hasFocus = true;
7153             this.startValue = this.getValue();
7154             this.fireEvent("focus", this);
7155         }
7156     },
7157     
7158     beforeBlur : Roo.emptyFn,
7159
7160     
7161     // private
7162     onBlur : function(){
7163         this.beforeBlur();
7164         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7165             //this.el.removeClass(this.focusClass);
7166         }
7167         this.hasFocus = false;
7168         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
7169             this.validate();
7170         }
7171         var v = this.getValue();
7172         if(String(v) !== String(this.startValue)){
7173             this.fireEvent('change', this, v, this.startValue);
7174         }
7175         this.fireEvent("blur", this);
7176     },
7177     
7178     /**
7179      * Resets the current field value to the originally loaded value and clears any validation messages
7180      */
7181     reset : function(){
7182         this.setValue(this.originalValue);
7183         this.clearInvalid();
7184     },
7185      /**
7186      * Returns the name of the field
7187      * @return {Mixed} name The name field
7188      */
7189     getName: function(){
7190         return this.name;
7191     },
7192      /**
7193      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
7194      * @return {Mixed} value The field value
7195      */
7196     getValue : function(){
7197         
7198         var v = this.inputEl().getValue();
7199         
7200         return v;
7201     },
7202     /**
7203      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
7204      * @return {Mixed} value The field value
7205      */
7206     getRawValue : function(){
7207         var v = this.inputEl().getValue();
7208         
7209         return v;
7210     },
7211     
7212     /**
7213      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
7214      * @param {Mixed} value The value to set
7215      */
7216     setRawValue : function(v){
7217         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7218     },
7219     
7220     selectText : function(start, end){
7221         var v = this.getRawValue();
7222         if(v.length > 0){
7223             start = start === undefined ? 0 : start;
7224             end = end === undefined ? v.length : end;
7225             var d = this.inputEl().dom;
7226             if(d.setSelectionRange){
7227                 d.setSelectionRange(start, end);
7228             }else if(d.createTextRange){
7229                 var range = d.createTextRange();
7230                 range.moveStart("character", start);
7231                 range.moveEnd("character", v.length-end);
7232                 range.select();
7233             }
7234         }
7235     },
7236     
7237     /**
7238      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
7239      * @param {Mixed} value The value to set
7240      */
7241     setValue : function(v){
7242         this.value = v;
7243         if(this.rendered){
7244             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7245             this.validate();
7246         }
7247     },
7248     
7249     /*
7250     processValue : function(value){
7251         if(this.stripCharsRe){
7252             var newValue = value.replace(this.stripCharsRe, '');
7253             if(newValue !== value){
7254                 this.setRawValue(newValue);
7255                 return newValue;
7256             }
7257         }
7258         return value;
7259     },
7260   */
7261     preFocus : function(){
7262         
7263         if(this.selectOnFocus){
7264             this.inputEl().dom.select();
7265         }
7266     },
7267     filterKeys : function(e){
7268         var k = e.getKey();
7269         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
7270             return;
7271         }
7272         var c = e.getCharCode(), cc = String.fromCharCode(c);
7273         if(Roo.isIE && (e.isSpecialKey() || !cc)){
7274             return;
7275         }
7276         if(!this.maskRe.test(cc)){
7277             e.stopEvent();
7278         }
7279     },
7280      /**
7281      * Clear any invalid styles/messages for this field
7282      */
7283     clearInvalid : function(){
7284         
7285         if(!this.el || this.preventMark){ // not rendered
7286             return;
7287         }
7288         this.el.removeClass(this.invalidClass);
7289         /*
7290         switch(this.msgTarget){
7291             case 'qtip':
7292                 this.el.dom.qtip = '';
7293                 break;
7294             case 'title':
7295                 this.el.dom.title = '';
7296                 break;
7297             case 'under':
7298                 if(this.errorEl){
7299                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
7300                 }
7301                 break;
7302             case 'side':
7303                 if(this.errorIcon){
7304                     this.errorIcon.dom.qtip = '';
7305                     this.errorIcon.hide();
7306                     this.un('resize', this.alignErrorIcon, this);
7307                 }
7308                 break;
7309             default:
7310                 var t = Roo.getDom(this.msgTarget);
7311                 t.innerHTML = '';
7312                 t.style.display = 'none';
7313                 break;
7314         }
7315         */
7316         this.fireEvent('valid', this);
7317     },
7318      /**
7319      * Mark this field as invalid
7320      * @param {String} msg The validation message
7321      */
7322     markInvalid : function(msg){
7323         if(!this.el  || this.preventMark){ // not rendered
7324             return;
7325         }
7326         this.el.addClass(this.invalidClass);
7327         /*
7328         msg = msg || this.invalidText;
7329         switch(this.msgTarget){
7330             case 'qtip':
7331                 this.el.dom.qtip = msg;
7332                 this.el.dom.qclass = 'x-form-invalid-tip';
7333                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
7334                     Roo.QuickTips.enable();
7335                 }
7336                 break;
7337             case 'title':
7338                 this.el.dom.title = msg;
7339                 break;
7340             case 'under':
7341                 if(!this.errorEl){
7342                     var elp = this.el.findParent('.x-form-element', 5, true);
7343                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
7344                     this.errorEl.setWidth(elp.getWidth(true)-20);
7345                 }
7346                 this.errorEl.update(msg);
7347                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
7348                 break;
7349             case 'side':
7350                 if(!this.errorIcon){
7351                     var elp = this.el.findParent('.x-form-element', 5, true);
7352                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
7353                 }
7354                 this.alignErrorIcon();
7355                 this.errorIcon.dom.qtip = msg;
7356                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
7357                 this.errorIcon.show();
7358                 this.on('resize', this.alignErrorIcon, this);
7359                 break;
7360             default:
7361                 var t = Roo.getDom(this.msgTarget);
7362                 t.innerHTML = msg;
7363                 t.style.display = this.msgDisplay;
7364                 break;
7365         }
7366         */
7367         this.fireEvent('invalid', this, msg);
7368     },
7369     // private
7370     SafariOnKeyDown : function(event)
7371     {
7372         // this is a workaround for a password hang bug on chrome/ webkit.
7373         
7374         var isSelectAll = false;
7375         
7376         if(this.inputEl().dom.selectionEnd > 0){
7377             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
7378         }
7379         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
7380             event.preventDefault();
7381             this.setValue('');
7382             return;
7383         }
7384         
7385         if(isSelectAll){ // backspace and delete key
7386             
7387             event.preventDefault();
7388             // this is very hacky as keydown always get's upper case.
7389             //
7390             var cc = String.fromCharCode(event.getCharCode());
7391             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
7392             
7393         }
7394     },
7395     adjustWidth : function(tag, w){
7396         tag = tag.toLowerCase();
7397         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
7398             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
7399                 if(tag == 'input'){
7400                     return w + 2;
7401                 }
7402                 if(tag == 'textarea'){
7403                     return w-2;
7404                 }
7405             }else if(Roo.isOpera){
7406                 if(tag == 'input'){
7407                     return w + 2;
7408                 }
7409                 if(tag == 'textarea'){
7410                     return w-2;
7411                 }
7412             }
7413         }
7414         return w;
7415     }
7416     
7417 });
7418
7419  
7420 /*
7421  * - LGPL
7422  *
7423  * Input
7424  * 
7425  */
7426
7427 /**
7428  * @class Roo.bootstrap.TextArea
7429  * @extends Roo.bootstrap.Input
7430  * Bootstrap TextArea class
7431  * @cfg {Number} cols Specifies the visible width of a text area
7432  * @cfg {Number} rows Specifies the visible number of lines in a text area
7433  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
7434  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
7435  * @cfg {string} html text
7436  * 
7437  * @constructor
7438  * Create a new TextArea
7439  * @param {Object} config The config object
7440  */
7441
7442 Roo.bootstrap.TextArea = function(config){
7443     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
7444    
7445 };
7446
7447 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
7448      
7449     cols : false,
7450     rows : 5,
7451     readOnly : false,
7452     warp : 'soft',
7453     resize : false,
7454     value: false,
7455     html: false,
7456     
7457     getAutoCreate : function(){
7458         
7459         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7460         
7461         var id = Roo.id();
7462         
7463         var cfg = {};
7464         
7465         var input =  {
7466             tag: 'textarea',
7467             id : id,
7468             warp : this.warp,
7469             rows : this.rows,
7470             value : this.value || '',
7471             html: this.html || '',
7472             cls : 'form-control',
7473             placeholder : this.placeholder || '' 
7474             
7475         };
7476         
7477         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7478             input.maxLength = this.maxLength;
7479         }
7480         
7481         if(this.resize){
7482             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
7483         }
7484         
7485         if(this.cols){
7486             input.cols = this.cols;
7487         }
7488         
7489         if (this.readOnly) {
7490             input.readonly = true;
7491         }
7492         
7493         if (this.name) {
7494             input.name = this.name;
7495         }
7496         
7497         if (this.size) {
7498             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
7499         }
7500         
7501         var settings=this;
7502         ['xs','sm','md','lg'].map(function(size){
7503             if (settings[size]) {
7504                 cfg.cls += ' col-' + size + '-' + settings[size];
7505             }
7506         });
7507         
7508         var inputblock = input;
7509         
7510         if (this.before || this.after) {
7511             
7512             inputblock = {
7513                 cls : 'input-group',
7514                 cn :  [] 
7515             };
7516             if (this.before) {
7517                 inputblock.cn.push({
7518                     tag :'span',
7519                     cls : 'input-group-addon',
7520                     html : this.before
7521                 });
7522             }
7523             inputblock.cn.push(input);
7524             if (this.after) {
7525                 inputblock.cn.push({
7526                     tag :'span',
7527                     cls : 'input-group-addon',
7528                     html : this.after
7529                 });
7530             }
7531             
7532         }
7533         
7534         if (align ==='left' && this.fieldLabel.length) {
7535                 Roo.log("left and has label");
7536                 cfg.cn = [
7537                     
7538                     {
7539                         tag: 'label',
7540                         'for' :  id,
7541                         cls : 'control-label col-sm-' + this.labelWidth,
7542                         html : this.fieldLabel
7543                         
7544                     },
7545                     {
7546                         cls : "col-sm-" + (12 - this.labelWidth), 
7547                         cn: [
7548                             inputblock
7549                         ]
7550                     }
7551                     
7552                 ];
7553         } else if ( this.fieldLabel.length) {
7554                 Roo.log(" label");
7555                  cfg.cn = [
7556                    
7557                     {
7558                         tag: 'label',
7559                         //cls : 'input-group-addon',
7560                         html : this.fieldLabel
7561                         
7562                     },
7563                     
7564                     inputblock
7565                     
7566                 ];
7567
7568         } else {
7569             
7570                    Roo.log(" no label && no align");
7571                 cfg.cn = [
7572                     
7573                         inputblock
7574                     
7575                 ];
7576                 
7577                 
7578         }
7579         
7580         if (this.disabled) {
7581             input.disabled=true;
7582         }
7583         
7584         return cfg;
7585         
7586     },
7587     /**
7588      * return the real textarea element.
7589      */
7590     inputEl: function ()
7591     {
7592         return this.el.select('textarea.form-control',true).first();
7593     }
7594 });
7595
7596  
7597 /*
7598  * - LGPL
7599  *
7600  * trigger field - base class for combo..
7601  * 
7602  */
7603  
7604 /**
7605  * @class Roo.bootstrap.TriggerField
7606  * @extends Roo.bootstrap.Input
7607  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
7608  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
7609  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
7610  * for which you can provide a custom implementation.  For example:
7611  * <pre><code>
7612 var trigger = new Roo.bootstrap.TriggerField();
7613 trigger.onTriggerClick = myTriggerFn;
7614 trigger.applyTo('my-field');
7615 </code></pre>
7616  *
7617  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
7618  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
7619  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
7620  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
7621  * @constructor
7622  * Create a new TriggerField.
7623  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
7624  * to the base TextField)
7625  */
7626 Roo.bootstrap.TriggerField = function(config){
7627     this.mimicing = false;
7628     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
7629 };
7630
7631 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
7632     /**
7633      * @cfg {String} triggerClass A CSS class to apply to the trigger
7634      */
7635      /**
7636      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
7637      */
7638     hideTrigger:false,
7639
7640     /** @cfg {Boolean} grow @hide */
7641     /** @cfg {Number} growMin @hide */
7642     /** @cfg {Number} growMax @hide */
7643
7644     /**
7645      * @hide 
7646      * @method
7647      */
7648     autoSize: Roo.emptyFn,
7649     // private
7650     monitorTab : true,
7651     // private
7652     deferHeight : true,
7653
7654     
7655     actionMode : 'wrap',
7656     
7657     
7658     
7659     getAutoCreate : function(){
7660        
7661         var parent = this.parent();
7662         
7663         var align = this.labelAlign || this.parentLabelAlign();
7664         
7665         var id = Roo.id();
7666         
7667         var cfg = {
7668             cls: 'form-group' //input-group
7669         };
7670         
7671         
7672         var input =  {
7673             tag: 'input',
7674             id : id,
7675             type : this.inputType,
7676             cls : 'form-control',
7677             autocomplete: 'off',
7678             placeholder : this.placeholder || '' 
7679             
7680         };
7681         if (this.name) {
7682             input.name = this.name;
7683         }
7684         if (this.size) {
7685             input.cls += ' input-' + this.size;
7686         }
7687         
7688         if (this.disabled) {
7689             input.disabled=true;
7690         }
7691         
7692         var inputblock = input;
7693         
7694         if (this.before || this.after) {
7695             
7696             inputblock = {
7697                 cls : 'input-group',
7698                 cn :  [] 
7699             };
7700             if (this.before) {
7701                 inputblock.cn.push({
7702                     tag :'span',
7703                     cls : 'input-group-addon',
7704                     html : this.before
7705                 });
7706             }
7707             inputblock.cn.push(input);
7708             if (this.after) {
7709                 inputblock.cn.push({
7710                     tag :'span',
7711                     cls : 'input-group-addon',
7712                     html : this.after
7713                 });
7714             }
7715             
7716         };
7717         
7718         var box = {
7719             tag: 'div',
7720             cn: [
7721                 {
7722                     tag: 'input',
7723                     type : 'hidden',
7724                     cls: 'form-hidden-field'
7725                 },
7726                 inputblock
7727             ]
7728             
7729         };
7730         
7731         if(this.multiple){
7732             Roo.log('multiple');
7733             
7734             box = {
7735                 tag: 'div',
7736                 cn: [
7737                     {
7738                         tag: 'input',
7739                         type : 'hidden',
7740                         cls: 'form-hidden-field'
7741                     },
7742                     {
7743                         tag: 'ul',
7744                         cls: 'select2-choices',
7745                         cn:[
7746                             {
7747                                 tag: 'li',
7748                                 cls: 'select2-search-field',
7749                                 cn: [
7750
7751                                     inputblock
7752                                 ]
7753                             }
7754                         ]
7755                     }
7756                 ]
7757             }
7758         };
7759         
7760         var combobox = {
7761             cls: 'select2-container input-group',
7762             cn: [
7763                 box,
7764                 {
7765                     tag: 'ul',
7766                     cls: 'typeahead typeahead-long dropdown-menu',
7767                     style: 'display:none'
7768                 }
7769             ]
7770         };
7771         
7772         if(!this.multiple){
7773             combobox.cn.push({
7774                 tag :'span',
7775                 cls : 'input-group-addon btn dropdown-toggle',
7776                 cn : [
7777                     {
7778                         tag: 'span',
7779                         cls: 'caret'
7780                     },
7781                     {
7782                         tag: 'span',
7783                         cls: 'combobox-clear',
7784                         cn  : [
7785                             {
7786                                 tag : 'i',
7787                                 cls: 'icon-remove'
7788                             }
7789                         ]
7790                     }
7791                 ]
7792
7793             })
7794         }
7795         
7796         if(this.multiple){
7797             combobox.cls += ' select2-container-multi';
7798         }
7799         
7800         if (align ==='left' && this.fieldLabel.length) {
7801             
7802                 Roo.log("left and has label");
7803                 cfg.cn = [
7804                     
7805                     {
7806                         tag: 'label',
7807                         'for' :  id,
7808                         cls : 'control-label col-sm-' + this.labelWidth,
7809                         html : this.fieldLabel
7810                         
7811                     },
7812                     {
7813                         cls : "col-sm-" + (12 - this.labelWidth), 
7814                         cn: [
7815                             combobox
7816                         ]
7817                     }
7818                     
7819                 ];
7820         } else if ( this.fieldLabel.length) {
7821                 Roo.log(" label");
7822                  cfg.cn = [
7823                    
7824                     {
7825                         tag: 'label',
7826                         //cls : 'input-group-addon',
7827                         html : this.fieldLabel
7828                         
7829                     },
7830                     
7831                     combobox
7832                     
7833                 ];
7834
7835         } else {
7836             
7837                 Roo.log(" no label && no align");
7838                 cfg = combobox
7839                      
7840                 
7841         }
7842          
7843         var settings=this;
7844         ['xs','sm','md','lg'].map(function(size){
7845             if (settings[size]) {
7846                 cfg.cls += ' col-' + size + '-' + settings[size];
7847             }
7848         });
7849         
7850         return cfg;
7851         
7852     },
7853     
7854     
7855     
7856     // private
7857     onResize : function(w, h){
7858 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
7859 //        if(typeof w == 'number'){
7860 //            var x = w - this.trigger.getWidth();
7861 //            this.inputEl().setWidth(this.adjustWidth('input', x));
7862 //            this.trigger.setStyle('left', x+'px');
7863 //        }
7864     },
7865
7866     // private
7867     adjustSize : Roo.BoxComponent.prototype.adjustSize,
7868
7869     // private
7870     getResizeEl : function(){
7871         return this.inputEl();
7872     },
7873
7874     // private
7875     getPositionEl : function(){
7876         return this.inputEl();
7877     },
7878
7879     // private
7880     alignErrorIcon : function(){
7881         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
7882     },
7883
7884     // private
7885     initEvents : function(){
7886         
7887         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
7888         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
7889         if(!this.multiple){
7890             this.trigger = this.el.select('span.dropdown-toggle',true).first();
7891             if(this.hideTrigger){
7892                 this.trigger.setDisplayed(false);
7893             }
7894             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
7895         }
7896         
7897         if(this.multiple){
7898             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
7899         }
7900         
7901         //this.trigger.addClassOnOver('x-form-trigger-over');
7902         //this.trigger.addClassOnClick('x-form-trigger-click');
7903         
7904         //if(!this.width){
7905         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
7906         //}
7907     },
7908
7909     // private
7910     initTrigger : function(){
7911        
7912     },
7913
7914     // private
7915     onDestroy : function(){
7916         if(this.trigger){
7917             this.trigger.removeAllListeners();
7918           //  this.trigger.remove();
7919         }
7920         //if(this.wrap){
7921         //    this.wrap.remove();
7922         //}
7923         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
7924     },
7925
7926     // private
7927     onFocus : function(){
7928         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
7929         /*
7930         if(!this.mimicing){
7931             this.wrap.addClass('x-trigger-wrap-focus');
7932             this.mimicing = true;
7933             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
7934             if(this.monitorTab){
7935                 this.el.on("keydown", this.checkTab, this);
7936             }
7937         }
7938         */
7939     },
7940
7941     // private
7942     checkTab : function(e){
7943         if(e.getKey() == e.TAB){
7944             this.triggerBlur();
7945         }
7946     },
7947
7948     // private
7949     onBlur : function(){
7950         // do nothing
7951     },
7952
7953     // private
7954     mimicBlur : function(e, t){
7955         /*
7956         if(!this.wrap.contains(t) && this.validateBlur()){
7957             this.triggerBlur();
7958         }
7959         */
7960     },
7961
7962     // private
7963     triggerBlur : function(){
7964         this.mimicing = false;
7965         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
7966         if(this.monitorTab){
7967             this.el.un("keydown", this.checkTab, this);
7968         }
7969         //this.wrap.removeClass('x-trigger-wrap-focus');
7970         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
7971     },
7972
7973     // private
7974     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
7975     validateBlur : function(e, t){
7976         return true;
7977     },
7978
7979     // private
7980     onDisable : function(){
7981         this.inputEl().dom.disabled = true;
7982         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
7983         //if(this.wrap){
7984         //    this.wrap.addClass('x-item-disabled');
7985         //}
7986     },
7987
7988     // private
7989     onEnable : function(){
7990         this.inputEl().dom.disabled = false;
7991         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
7992         //if(this.wrap){
7993         //    this.el.removeClass('x-item-disabled');
7994         //}
7995     },
7996
7997     // private
7998     onShow : function(){
7999         var ae = this.getActionEl();
8000         
8001         if(ae){
8002             ae.dom.style.display = '';
8003             ae.dom.style.visibility = 'visible';
8004         }
8005     },
8006
8007     // private
8008     
8009     onHide : function(){
8010         var ae = this.getActionEl();
8011         ae.dom.style.display = 'none';
8012     },
8013
8014     /**
8015      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
8016      * by an implementing function.
8017      * @method
8018      * @param {EventObject} e
8019      */
8020     onTriggerClick : Roo.emptyFn
8021 });
8022  /*
8023  * Based on:
8024  * Ext JS Library 1.1.1
8025  * Copyright(c) 2006-2007, Ext JS, LLC.
8026  *
8027  * Originally Released Under LGPL - original licence link has changed is not relivant.
8028  *
8029  * Fork - LGPL
8030  * <script type="text/javascript">
8031  */
8032
8033
8034 /**
8035  * @class Roo.data.SortTypes
8036  * @singleton
8037  * Defines the default sorting (casting?) comparison functions used when sorting data.
8038  */
8039 Roo.data.SortTypes = {
8040     /**
8041      * Default sort that does nothing
8042      * @param {Mixed} s The value being converted
8043      * @return {Mixed} The comparison value
8044      */
8045     none : function(s){
8046         return s;
8047     },
8048     
8049     /**
8050      * The regular expression used to strip tags
8051      * @type {RegExp}
8052      * @property
8053      */
8054     stripTagsRE : /<\/?[^>]+>/gi,
8055     
8056     /**
8057      * Strips all HTML tags to sort on text only
8058      * @param {Mixed} s The value being converted
8059      * @return {String} The comparison value
8060      */
8061     asText : function(s){
8062         return String(s).replace(this.stripTagsRE, "");
8063     },
8064     
8065     /**
8066      * Strips all HTML tags to sort on text only - Case insensitive
8067      * @param {Mixed} s The value being converted
8068      * @return {String} The comparison value
8069      */
8070     asUCText : function(s){
8071         return String(s).toUpperCase().replace(this.stripTagsRE, "");
8072     },
8073     
8074     /**
8075      * Case insensitive string
8076      * @param {Mixed} s The value being converted
8077      * @return {String} The comparison value
8078      */
8079     asUCString : function(s) {
8080         return String(s).toUpperCase();
8081     },
8082     
8083     /**
8084      * Date sorting
8085      * @param {Mixed} s The value being converted
8086      * @return {Number} The comparison value
8087      */
8088     asDate : function(s) {
8089         if(!s){
8090             return 0;
8091         }
8092         if(s instanceof Date){
8093             return s.getTime();
8094         }
8095         return Date.parse(String(s));
8096     },
8097     
8098     /**
8099      * Float sorting
8100      * @param {Mixed} s The value being converted
8101      * @return {Float} The comparison value
8102      */
8103     asFloat : function(s) {
8104         var val = parseFloat(String(s).replace(/,/g, ""));
8105         if(isNaN(val)) val = 0;
8106         return val;
8107     },
8108     
8109     /**
8110      * Integer sorting
8111      * @param {Mixed} s The value being converted
8112      * @return {Number} The comparison value
8113      */
8114     asInt : function(s) {
8115         var val = parseInt(String(s).replace(/,/g, ""));
8116         if(isNaN(val)) val = 0;
8117         return val;
8118     }
8119 };/*
8120  * Based on:
8121  * Ext JS Library 1.1.1
8122  * Copyright(c) 2006-2007, Ext JS, LLC.
8123  *
8124  * Originally Released Under LGPL - original licence link has changed is not relivant.
8125  *
8126  * Fork - LGPL
8127  * <script type="text/javascript">
8128  */
8129
8130 /**
8131 * @class Roo.data.Record
8132  * Instances of this class encapsulate both record <em>definition</em> information, and record
8133  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
8134  * to access Records cached in an {@link Roo.data.Store} object.<br>
8135  * <p>
8136  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
8137  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
8138  * objects.<br>
8139  * <p>
8140  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
8141  * @constructor
8142  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
8143  * {@link #create}. The parameters are the same.
8144  * @param {Array} data An associative Array of data values keyed by the field name.
8145  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
8146  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
8147  * not specified an integer id is generated.
8148  */
8149 Roo.data.Record = function(data, id){
8150     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
8151     this.data = data;
8152 };
8153
8154 /**
8155  * Generate a constructor for a specific record layout.
8156  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
8157  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
8158  * Each field definition object may contain the following properties: <ul>
8159  * <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,
8160  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
8161  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
8162  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
8163  * is being used, then this is a string containing the javascript expression to reference the data relative to 
8164  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
8165  * to the data item relative to the record element. If the mapping expression is the same as the field name,
8166  * this may be omitted.</p></li>
8167  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
8168  * <ul><li>auto (Default, implies no conversion)</li>
8169  * <li>string</li>
8170  * <li>int</li>
8171  * <li>float</li>
8172  * <li>boolean</li>
8173  * <li>date</li></ul></p></li>
8174  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
8175  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
8176  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
8177  * by the Reader into an object that will be stored in the Record. It is passed the
8178  * following parameters:<ul>
8179  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
8180  * </ul></p></li>
8181  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
8182  * </ul>
8183  * <br>usage:<br><pre><code>
8184 var TopicRecord = Roo.data.Record.create(
8185     {name: 'title', mapping: 'topic_title'},
8186     {name: 'author', mapping: 'username'},
8187     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
8188     {name: 'lastPost', mapping: 'post_time', type: 'date'},
8189     {name: 'lastPoster', mapping: 'user2'},
8190     {name: 'excerpt', mapping: 'post_text'}
8191 );
8192
8193 var myNewRecord = new TopicRecord({
8194     title: 'Do my job please',
8195     author: 'noobie',
8196     totalPosts: 1,
8197     lastPost: new Date(),
8198     lastPoster: 'Animal',
8199     excerpt: 'No way dude!'
8200 });
8201 myStore.add(myNewRecord);
8202 </code></pre>
8203  * @method create
8204  * @static
8205  */
8206 Roo.data.Record.create = function(o){
8207     var f = function(){
8208         f.superclass.constructor.apply(this, arguments);
8209     };
8210     Roo.extend(f, Roo.data.Record);
8211     var p = f.prototype;
8212     p.fields = new Roo.util.MixedCollection(false, function(field){
8213         return field.name;
8214     });
8215     for(var i = 0, len = o.length; i < len; i++){
8216         p.fields.add(new Roo.data.Field(o[i]));
8217     }
8218     f.getField = function(name){
8219         return p.fields.get(name);  
8220     };
8221     return f;
8222 };
8223
8224 Roo.data.Record.AUTO_ID = 1000;
8225 Roo.data.Record.EDIT = 'edit';
8226 Roo.data.Record.REJECT = 'reject';
8227 Roo.data.Record.COMMIT = 'commit';
8228
8229 Roo.data.Record.prototype = {
8230     /**
8231      * Readonly flag - true if this record has been modified.
8232      * @type Boolean
8233      */
8234     dirty : false,
8235     editing : false,
8236     error: null,
8237     modified: null,
8238
8239     // private
8240     join : function(store){
8241         this.store = store;
8242     },
8243
8244     /**
8245      * Set the named field to the specified value.
8246      * @param {String} name The name of the field to set.
8247      * @param {Object} value The value to set the field to.
8248      */
8249     set : function(name, value){
8250         if(this.data[name] == value){
8251             return;
8252         }
8253         this.dirty = true;
8254         if(!this.modified){
8255             this.modified = {};
8256         }
8257         if(typeof this.modified[name] == 'undefined'){
8258             this.modified[name] = this.data[name];
8259         }
8260         this.data[name] = value;
8261         if(!this.editing && this.store){
8262             this.store.afterEdit(this);
8263         }       
8264     },
8265
8266     /**
8267      * Get the value of the named field.
8268      * @param {String} name The name of the field to get the value of.
8269      * @return {Object} The value of the field.
8270      */
8271     get : function(name){
8272         return this.data[name]; 
8273     },
8274
8275     // private
8276     beginEdit : function(){
8277         this.editing = true;
8278         this.modified = {}; 
8279     },
8280
8281     // private
8282     cancelEdit : function(){
8283         this.editing = false;
8284         delete this.modified;
8285     },
8286
8287     // private
8288     endEdit : function(){
8289         this.editing = false;
8290         if(this.dirty && this.store){
8291             this.store.afterEdit(this);
8292         }
8293     },
8294
8295     /**
8296      * Usually called by the {@link Roo.data.Store} which owns the Record.
8297      * Rejects all changes made to the Record since either creation, or the last commit operation.
8298      * Modified fields are reverted to their original values.
8299      * <p>
8300      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8301      * of reject operations.
8302      */
8303     reject : function(){
8304         var m = this.modified;
8305         for(var n in m){
8306             if(typeof m[n] != "function"){
8307                 this.data[n] = m[n];
8308             }
8309         }
8310         this.dirty = false;
8311         delete this.modified;
8312         this.editing = false;
8313         if(this.store){
8314             this.store.afterReject(this);
8315         }
8316     },
8317
8318     /**
8319      * Usually called by the {@link Roo.data.Store} which owns the Record.
8320      * Commits all changes made to the Record since either creation, or the last commit operation.
8321      * <p>
8322      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8323      * of commit operations.
8324      */
8325     commit : function(){
8326         this.dirty = false;
8327         delete this.modified;
8328         this.editing = false;
8329         if(this.store){
8330             this.store.afterCommit(this);
8331         }
8332     },
8333
8334     // private
8335     hasError : function(){
8336         return this.error != null;
8337     },
8338
8339     // private
8340     clearError : function(){
8341         this.error = null;
8342     },
8343
8344     /**
8345      * Creates a copy of this record.
8346      * @param {String} id (optional) A new record id if you don't want to use this record's id
8347      * @return {Record}
8348      */
8349     copy : function(newId) {
8350         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
8351     }
8352 };/*
8353  * Based on:
8354  * Ext JS Library 1.1.1
8355  * Copyright(c) 2006-2007, Ext JS, LLC.
8356  *
8357  * Originally Released Under LGPL - original licence link has changed is not relivant.
8358  *
8359  * Fork - LGPL
8360  * <script type="text/javascript">
8361  */
8362
8363
8364
8365 /**
8366  * @class Roo.data.Store
8367  * @extends Roo.util.Observable
8368  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
8369  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
8370  * <p>
8371  * 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
8372  * has no knowledge of the format of the data returned by the Proxy.<br>
8373  * <p>
8374  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
8375  * instances from the data object. These records are cached and made available through accessor functions.
8376  * @constructor
8377  * Creates a new Store.
8378  * @param {Object} config A config object containing the objects needed for the Store to access data,
8379  * and read the data into Records.
8380  */
8381 Roo.data.Store = function(config){
8382     this.data = new Roo.util.MixedCollection(false);
8383     this.data.getKey = function(o){
8384         return o.id;
8385     };
8386     this.baseParams = {};
8387     // private
8388     this.paramNames = {
8389         "start" : "start",
8390         "limit" : "limit",
8391         "sort" : "sort",
8392         "dir" : "dir",
8393         "multisort" : "_multisort"
8394     };
8395
8396     if(config && config.data){
8397         this.inlineData = config.data;
8398         delete config.data;
8399     }
8400
8401     Roo.apply(this, config);
8402     
8403     if(this.reader){ // reader passed
8404         this.reader = Roo.factory(this.reader, Roo.data);
8405         this.reader.xmodule = this.xmodule || false;
8406         if(!this.recordType){
8407             this.recordType = this.reader.recordType;
8408         }
8409         if(this.reader.onMetaChange){
8410             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
8411         }
8412     }
8413
8414     if(this.recordType){
8415         this.fields = this.recordType.prototype.fields;
8416     }
8417     this.modified = [];
8418
8419     this.addEvents({
8420         /**
8421          * @event datachanged
8422          * Fires when the data cache has changed, and a widget which is using this Store
8423          * as a Record cache should refresh its view.
8424          * @param {Store} this
8425          */
8426         datachanged : true,
8427         /**
8428          * @event metachange
8429          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
8430          * @param {Store} this
8431          * @param {Object} meta The JSON metadata
8432          */
8433         metachange : true,
8434         /**
8435          * @event add
8436          * Fires when Records have been added to the Store
8437          * @param {Store} this
8438          * @param {Roo.data.Record[]} records The array of Records added
8439          * @param {Number} index The index at which the record(s) were added
8440          */
8441         add : true,
8442         /**
8443          * @event remove
8444          * Fires when a Record has been removed from the Store
8445          * @param {Store} this
8446          * @param {Roo.data.Record} record The Record that was removed
8447          * @param {Number} index The index at which the record was removed
8448          */
8449         remove : true,
8450         /**
8451          * @event update
8452          * Fires when a Record has been updated
8453          * @param {Store} this
8454          * @param {Roo.data.Record} record The Record that was updated
8455          * @param {String} operation The update operation being performed.  Value may be one of:
8456          * <pre><code>
8457  Roo.data.Record.EDIT
8458  Roo.data.Record.REJECT
8459  Roo.data.Record.COMMIT
8460          * </code></pre>
8461          */
8462         update : true,
8463         /**
8464          * @event clear
8465          * Fires when the data cache has been cleared.
8466          * @param {Store} this
8467          */
8468         clear : true,
8469         /**
8470          * @event beforeload
8471          * Fires before a request is made for a new data object.  If the beforeload handler returns false
8472          * the load action will be canceled.
8473          * @param {Store} this
8474          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8475          */
8476         beforeload : true,
8477         /**
8478          * @event beforeloadadd
8479          * Fires after a new set of Records has been loaded.
8480          * @param {Store} this
8481          * @param {Roo.data.Record[]} records The Records that were loaded
8482          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8483          */
8484         beforeloadadd : true,
8485         /**
8486          * @event load
8487          * Fires after a new set of Records has been loaded, before they are added to the store.
8488          * @param {Store} this
8489          * @param {Roo.data.Record[]} records The Records that were loaded
8490          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8491          * @params {Object} return from reader
8492          */
8493         load : true,
8494         /**
8495          * @event loadexception
8496          * Fires if an exception occurs in the Proxy during loading.
8497          * Called with the signature of the Proxy's "loadexception" event.
8498          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
8499          * 
8500          * @param {Proxy} 
8501          * @param {Object} return from JsonData.reader() - success, totalRecords, records
8502          * @param {Object} load options 
8503          * @param {Object} jsonData from your request (normally this contains the Exception)
8504          */
8505         loadexception : true
8506     });
8507     
8508     if(this.proxy){
8509         this.proxy = Roo.factory(this.proxy, Roo.data);
8510         this.proxy.xmodule = this.xmodule || false;
8511         this.relayEvents(this.proxy,  ["loadexception"]);
8512     }
8513     this.sortToggle = {};
8514     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
8515
8516     Roo.data.Store.superclass.constructor.call(this);
8517
8518     if(this.inlineData){
8519         this.loadData(this.inlineData);
8520         delete this.inlineData;
8521     }
8522 };
8523
8524 Roo.extend(Roo.data.Store, Roo.util.Observable, {
8525      /**
8526     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
8527     * without a remote query - used by combo/forms at present.
8528     */
8529     
8530     /**
8531     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
8532     */
8533     /**
8534     * @cfg {Array} data Inline data to be loaded when the store is initialized.
8535     */
8536     /**
8537     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
8538     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
8539     */
8540     /**
8541     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
8542     * on any HTTP request
8543     */
8544     /**
8545     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
8546     */
8547     /**
8548     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
8549     */
8550     multiSort: false,
8551     /**
8552     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
8553     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
8554     */
8555     remoteSort : false,
8556
8557     /**
8558     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
8559      * loaded or when a record is removed. (defaults to false).
8560     */
8561     pruneModifiedRecords : false,
8562
8563     // private
8564     lastOptions : null,
8565
8566     /**
8567      * Add Records to the Store and fires the add event.
8568      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8569      */
8570     add : function(records){
8571         records = [].concat(records);
8572         for(var i = 0, len = records.length; i < len; i++){
8573             records[i].join(this);
8574         }
8575         var index = this.data.length;
8576         this.data.addAll(records);
8577         this.fireEvent("add", this, records, index);
8578     },
8579
8580     /**
8581      * Remove a Record from the Store and fires the remove event.
8582      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
8583      */
8584     remove : function(record){
8585         var index = this.data.indexOf(record);
8586         this.data.removeAt(index);
8587         if(this.pruneModifiedRecords){
8588             this.modified.remove(record);
8589         }
8590         this.fireEvent("remove", this, record, index);
8591     },
8592
8593     /**
8594      * Remove all Records from the Store and fires the clear event.
8595      */
8596     removeAll : function(){
8597         this.data.clear();
8598         if(this.pruneModifiedRecords){
8599             this.modified = [];
8600         }
8601         this.fireEvent("clear", this);
8602     },
8603
8604     /**
8605      * Inserts Records to the Store at the given index and fires the add event.
8606      * @param {Number} index The start index at which to insert the passed Records.
8607      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8608      */
8609     insert : function(index, records){
8610         records = [].concat(records);
8611         for(var i = 0, len = records.length; i < len; i++){
8612             this.data.insert(index, records[i]);
8613             records[i].join(this);
8614         }
8615         this.fireEvent("add", this, records, index);
8616     },
8617
8618     /**
8619      * Get the index within the cache of the passed Record.
8620      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
8621      * @return {Number} The index of the passed Record. Returns -1 if not found.
8622      */
8623     indexOf : function(record){
8624         return this.data.indexOf(record);
8625     },
8626
8627     /**
8628      * Get the index within the cache of the Record with the passed id.
8629      * @param {String} id The id of the Record to find.
8630      * @return {Number} The index of the Record. Returns -1 if not found.
8631      */
8632     indexOfId : function(id){
8633         return this.data.indexOfKey(id);
8634     },
8635
8636     /**
8637      * Get the Record with the specified id.
8638      * @param {String} id The id of the Record to find.
8639      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
8640      */
8641     getById : function(id){
8642         return this.data.key(id);
8643     },
8644
8645     /**
8646      * Get the Record at the specified index.
8647      * @param {Number} index The index of the Record to find.
8648      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
8649      */
8650     getAt : function(index){
8651         return this.data.itemAt(index);
8652     },
8653
8654     /**
8655      * Returns a range of Records between specified indices.
8656      * @param {Number} startIndex (optional) The starting index (defaults to 0)
8657      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
8658      * @return {Roo.data.Record[]} An array of Records
8659      */
8660     getRange : function(start, end){
8661         return this.data.getRange(start, end);
8662     },
8663
8664     // private
8665     storeOptions : function(o){
8666         o = Roo.apply({}, o);
8667         delete o.callback;
8668         delete o.scope;
8669         this.lastOptions = o;
8670     },
8671
8672     /**
8673      * Loads the Record cache from the configured Proxy using the configured Reader.
8674      * <p>
8675      * If using remote paging, then the first load call must specify the <em>start</em>
8676      * and <em>limit</em> properties in the options.params property to establish the initial
8677      * position within the dataset, and the number of Records to cache on each read from the Proxy.
8678      * <p>
8679      * <strong>It is important to note that for remote data sources, loading is asynchronous,
8680      * and this call will return before the new data has been loaded. Perform any post-processing
8681      * in a callback function, or in a "load" event handler.</strong>
8682      * <p>
8683      * @param {Object} options An object containing properties which control loading options:<ul>
8684      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
8685      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
8686      * passed the following arguments:<ul>
8687      * <li>r : Roo.data.Record[]</li>
8688      * <li>options: Options object from the load call</li>
8689      * <li>success: Boolean success indicator</li></ul></li>
8690      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
8691      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
8692      * </ul>
8693      */
8694     load : function(options){
8695         options = options || {};
8696         if(this.fireEvent("beforeload", this, options) !== false){
8697             this.storeOptions(options);
8698             var p = Roo.apply(options.params || {}, this.baseParams);
8699             // if meta was not loaded from remote source.. try requesting it.
8700             if (!this.reader.metaFromRemote) {
8701                 p._requestMeta = 1;
8702             }
8703             if(this.sortInfo && this.remoteSort){
8704                 var pn = this.paramNames;
8705                 p[pn["sort"]] = this.sortInfo.field;
8706                 p[pn["dir"]] = this.sortInfo.direction;
8707             }
8708             if (this.multiSort) {
8709                 var pn = this.paramNames;
8710                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
8711             }
8712             
8713             this.proxy.load(p, this.reader, this.loadRecords, this, options);
8714         }
8715     },
8716
8717     /**
8718      * Reloads the Record cache from the configured Proxy using the configured Reader and
8719      * the options from the last load operation performed.
8720      * @param {Object} options (optional) An object containing properties which may override the options
8721      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
8722      * the most recently used options are reused).
8723      */
8724     reload : function(options){
8725         this.load(Roo.applyIf(options||{}, this.lastOptions));
8726     },
8727
8728     // private
8729     // Called as a callback by the Reader during a load operation.
8730     loadRecords : function(o, options, success){
8731         if(!o || success === false){
8732             if(success !== false){
8733                 this.fireEvent("load", this, [], options, o);
8734             }
8735             if(options.callback){
8736                 options.callback.call(options.scope || this, [], options, false);
8737             }
8738             return;
8739         }
8740         // if data returned failure - throw an exception.
8741         if (o.success === false) {
8742             // show a message if no listener is registered.
8743             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
8744                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
8745             }
8746             // loadmask wil be hooked into this..
8747             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
8748             return;
8749         }
8750         var r = o.records, t = o.totalRecords || r.length;
8751         
8752         this.fireEvent("beforeloadadd", this, r, options, o);
8753         
8754         if(!options || options.add !== true){
8755             if(this.pruneModifiedRecords){
8756                 this.modified = [];
8757             }
8758             for(var i = 0, len = r.length; i < len; i++){
8759                 r[i].join(this);
8760             }
8761             if(this.snapshot){
8762                 this.data = this.snapshot;
8763                 delete this.snapshot;
8764             }
8765             this.data.clear();
8766             this.data.addAll(r);
8767             this.totalLength = t;
8768             this.applySort();
8769             this.fireEvent("datachanged", this);
8770         }else{
8771             this.totalLength = Math.max(t, this.data.length+r.length);
8772             this.add(r);
8773         }
8774         this.fireEvent("load", this, r, options, o);
8775         if(options.callback){
8776             options.callback.call(options.scope || this, r, options, true);
8777         }
8778     },
8779
8780
8781     /**
8782      * Loads data from a passed data block. A Reader which understands the format of the data
8783      * must have been configured in the constructor.
8784      * @param {Object} data The data block from which to read the Records.  The format of the data expected
8785      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
8786      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
8787      */
8788     loadData : function(o, append){
8789         var r = this.reader.readRecords(o);
8790         this.loadRecords(r, {add: append}, true);
8791     },
8792
8793     /**
8794      * Gets the number of cached records.
8795      * <p>
8796      * <em>If using paging, this may not be the total size of the dataset. If the data object
8797      * used by the Reader contains the dataset size, then the getTotalCount() function returns
8798      * the data set size</em>
8799      */
8800     getCount : function(){
8801         return this.data.length || 0;
8802     },
8803
8804     /**
8805      * Gets the total number of records in the dataset as returned by the server.
8806      * <p>
8807      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
8808      * the dataset size</em>
8809      */
8810     getTotalCount : function(){
8811         return this.totalLength || 0;
8812     },
8813
8814     /**
8815      * Returns the sort state of the Store as an object with two properties:
8816      * <pre><code>
8817  field {String} The name of the field by which the Records are sorted
8818  direction {String} The sort order, "ASC" or "DESC"
8819      * </code></pre>
8820      */
8821     getSortState : function(){
8822         return this.sortInfo;
8823     },
8824
8825     // private
8826     applySort : function(){
8827         if(this.sortInfo && !this.remoteSort){
8828             var s = this.sortInfo, f = s.field;
8829             var st = this.fields.get(f).sortType;
8830             var fn = function(r1, r2){
8831                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
8832                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
8833             };
8834             this.data.sort(s.direction, fn);
8835             if(this.snapshot && this.snapshot != this.data){
8836                 this.snapshot.sort(s.direction, fn);
8837             }
8838         }
8839     },
8840
8841     /**
8842      * Sets the default sort column and order to be used by the next load operation.
8843      * @param {String} fieldName The name of the field to sort by.
8844      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
8845      */
8846     setDefaultSort : function(field, dir){
8847         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
8848     },
8849
8850     /**
8851      * Sort the Records.
8852      * If remote sorting is used, the sort is performed on the server, and the cache is
8853      * reloaded. If local sorting is used, the cache is sorted internally.
8854      * @param {String} fieldName The name of the field to sort by.
8855      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
8856      */
8857     sort : function(fieldName, dir){
8858         var f = this.fields.get(fieldName);
8859         if(!dir){
8860             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
8861             
8862             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
8863                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
8864             }else{
8865                 dir = f.sortDir;
8866             }
8867         }
8868         this.sortToggle[f.name] = dir;
8869         this.sortInfo = {field: f.name, direction: dir};
8870         if(!this.remoteSort){
8871             this.applySort();
8872             this.fireEvent("datachanged", this);
8873         }else{
8874             this.load(this.lastOptions);
8875         }
8876     },
8877
8878     /**
8879      * Calls the specified function for each of the Records in the cache.
8880      * @param {Function} fn The function to call. The Record is passed as the first parameter.
8881      * Returning <em>false</em> aborts and exits the iteration.
8882      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
8883      */
8884     each : function(fn, scope){
8885         this.data.each(fn, scope);
8886     },
8887
8888     /**
8889      * Gets all records modified since the last commit.  Modified records are persisted across load operations
8890      * (e.g., during paging).
8891      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
8892      */
8893     getModifiedRecords : function(){
8894         return this.modified;
8895     },
8896
8897     // private
8898     createFilterFn : function(property, value, anyMatch){
8899         if(!value.exec){ // not a regex
8900             value = String(value);
8901             if(value.length == 0){
8902                 return false;
8903             }
8904             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
8905         }
8906         return function(r){
8907             return value.test(r.data[property]);
8908         };
8909     },
8910
8911     /**
8912      * Sums the value of <i>property</i> for each record between start and end and returns the result.
8913      * @param {String} property A field on your records
8914      * @param {Number} start The record index to start at (defaults to 0)
8915      * @param {Number} end The last record index to include (defaults to length - 1)
8916      * @return {Number} The sum
8917      */
8918     sum : function(property, start, end){
8919         var rs = this.data.items, v = 0;
8920         start = start || 0;
8921         end = (end || end === 0) ? end : rs.length-1;
8922
8923         for(var i = start; i <= end; i++){
8924             v += (rs[i].data[property] || 0);
8925         }
8926         return v;
8927     },
8928
8929     /**
8930      * Filter the records by a specified property.
8931      * @param {String} field A field on your records
8932      * @param {String/RegExp} value Either a string that the field
8933      * should start with or a RegExp to test against the field
8934      * @param {Boolean} anyMatch True to match any part not just the beginning
8935      */
8936     filter : function(property, value, anyMatch){
8937         var fn = this.createFilterFn(property, value, anyMatch);
8938         return fn ? this.filterBy(fn) : this.clearFilter();
8939     },
8940
8941     /**
8942      * Filter by a function. The specified function will be called with each
8943      * record in this data source. If the function returns true the record is included,
8944      * otherwise it is filtered.
8945      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
8946      * @param {Object} scope (optional) The scope of the function (defaults to this)
8947      */
8948     filterBy : function(fn, scope){
8949         this.snapshot = this.snapshot || this.data;
8950         this.data = this.queryBy(fn, scope||this);
8951         this.fireEvent("datachanged", this);
8952     },
8953
8954     /**
8955      * Query the records by a specified property.
8956      * @param {String} field A field on your records
8957      * @param {String/RegExp} value Either a string that the field
8958      * should start with or a RegExp to test against the field
8959      * @param {Boolean} anyMatch True to match any part not just the beginning
8960      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
8961      */
8962     query : function(property, value, anyMatch){
8963         var fn = this.createFilterFn(property, value, anyMatch);
8964         return fn ? this.queryBy(fn) : this.data.clone();
8965     },
8966
8967     /**
8968      * Query by a function. The specified function will be called with each
8969      * record in this data source. If the function returns true the record is included
8970      * in the results.
8971      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
8972      * @param {Object} scope (optional) The scope of the function (defaults to this)
8973       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
8974      **/
8975     queryBy : function(fn, scope){
8976         var data = this.snapshot || this.data;
8977         return data.filterBy(fn, scope||this);
8978     },
8979
8980     /**
8981      * Collects unique values for a particular dataIndex from this store.
8982      * @param {String} dataIndex The property to collect
8983      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
8984      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
8985      * @return {Array} An array of the unique values
8986      **/
8987     collect : function(dataIndex, allowNull, bypassFilter){
8988         var d = (bypassFilter === true && this.snapshot) ?
8989                 this.snapshot.items : this.data.items;
8990         var v, sv, r = [], l = {};
8991         for(var i = 0, len = d.length; i < len; i++){
8992             v = d[i].data[dataIndex];
8993             sv = String(v);
8994             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
8995                 l[sv] = true;
8996                 r[r.length] = v;
8997             }
8998         }
8999         return r;
9000     },
9001
9002     /**
9003      * Revert to a view of the Record cache with no filtering applied.
9004      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
9005      */
9006     clearFilter : function(suppressEvent){
9007         if(this.snapshot && this.snapshot != this.data){
9008             this.data = this.snapshot;
9009             delete this.snapshot;
9010             if(suppressEvent !== true){
9011                 this.fireEvent("datachanged", this);
9012             }
9013         }
9014     },
9015
9016     // private
9017     afterEdit : function(record){
9018         if(this.modified.indexOf(record) == -1){
9019             this.modified.push(record);
9020         }
9021         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
9022     },
9023     
9024     // private
9025     afterReject : function(record){
9026         this.modified.remove(record);
9027         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
9028     },
9029
9030     // private
9031     afterCommit : function(record){
9032         this.modified.remove(record);
9033         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
9034     },
9035
9036     /**
9037      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
9038      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
9039      */
9040     commitChanges : function(){
9041         var m = this.modified.slice(0);
9042         this.modified = [];
9043         for(var i = 0, len = m.length; i < len; i++){
9044             m[i].commit();
9045         }
9046     },
9047
9048     /**
9049      * Cancel outstanding changes on all changed records.
9050      */
9051     rejectChanges : function(){
9052         var m = this.modified.slice(0);
9053         this.modified = [];
9054         for(var i = 0, len = m.length; i < len; i++){
9055             m[i].reject();
9056         }
9057     },
9058
9059     onMetaChange : function(meta, rtype, o){
9060         this.recordType = rtype;
9061         this.fields = rtype.prototype.fields;
9062         delete this.snapshot;
9063         this.sortInfo = meta.sortInfo || this.sortInfo;
9064         this.modified = [];
9065         this.fireEvent('metachange', this, this.reader.meta);
9066     },
9067     
9068     moveIndex : function(data, type)
9069     {
9070         var index = this.indexOf(data);
9071         
9072         var newIndex = index + type;
9073         
9074         this.remove(data);
9075         
9076         this.insert(newIndex, data);
9077         
9078     }
9079 });/*
9080  * Based on:
9081  * Ext JS Library 1.1.1
9082  * Copyright(c) 2006-2007, Ext JS, LLC.
9083  *
9084  * Originally Released Under LGPL - original licence link has changed is not relivant.
9085  *
9086  * Fork - LGPL
9087  * <script type="text/javascript">
9088  */
9089
9090 /**
9091  * @class Roo.data.SimpleStore
9092  * @extends Roo.data.Store
9093  * Small helper class to make creating Stores from Array data easier.
9094  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
9095  * @cfg {Array} fields An array of field definition objects, or field name strings.
9096  * @cfg {Array} data The multi-dimensional array of data
9097  * @constructor
9098  * @param {Object} config
9099  */
9100 Roo.data.SimpleStore = function(config){
9101     Roo.data.SimpleStore.superclass.constructor.call(this, {
9102         isLocal : true,
9103         reader: new Roo.data.ArrayReader({
9104                 id: config.id
9105             },
9106             Roo.data.Record.create(config.fields)
9107         ),
9108         proxy : new Roo.data.MemoryProxy(config.data)
9109     });
9110     this.load();
9111 };
9112 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
9113  * Based on:
9114  * Ext JS Library 1.1.1
9115  * Copyright(c) 2006-2007, Ext JS, LLC.
9116  *
9117  * Originally Released Under LGPL - original licence link has changed is not relivant.
9118  *
9119  * Fork - LGPL
9120  * <script type="text/javascript">
9121  */
9122
9123 /**
9124 /**
9125  * @extends Roo.data.Store
9126  * @class Roo.data.JsonStore
9127  * Small helper class to make creating Stores for JSON data easier. <br/>
9128 <pre><code>
9129 var store = new Roo.data.JsonStore({
9130     url: 'get-images.php',
9131     root: 'images',
9132     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
9133 });
9134 </code></pre>
9135  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
9136  * JsonReader and HttpProxy (unless inline data is provided).</b>
9137  * @cfg {Array} fields An array of field definition objects, or field name strings.
9138  * @constructor
9139  * @param {Object} config
9140  */
9141 Roo.data.JsonStore = function(c){
9142     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
9143         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
9144         reader: new Roo.data.JsonReader(c, c.fields)
9145     }));
9146 };
9147 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
9148  * Based on:
9149  * Ext JS Library 1.1.1
9150  * Copyright(c) 2006-2007, Ext JS, LLC.
9151  *
9152  * Originally Released Under LGPL - original licence link has changed is not relivant.
9153  *
9154  * Fork - LGPL
9155  * <script type="text/javascript">
9156  */
9157
9158  
9159 Roo.data.Field = function(config){
9160     if(typeof config == "string"){
9161         config = {name: config};
9162     }
9163     Roo.apply(this, config);
9164     
9165     if(!this.type){
9166         this.type = "auto";
9167     }
9168     
9169     var st = Roo.data.SortTypes;
9170     // named sortTypes are supported, here we look them up
9171     if(typeof this.sortType == "string"){
9172         this.sortType = st[this.sortType];
9173     }
9174     
9175     // set default sortType for strings and dates
9176     if(!this.sortType){
9177         switch(this.type){
9178             case "string":
9179                 this.sortType = st.asUCString;
9180                 break;
9181             case "date":
9182                 this.sortType = st.asDate;
9183                 break;
9184             default:
9185                 this.sortType = st.none;
9186         }
9187     }
9188
9189     // define once
9190     var stripRe = /[\$,%]/g;
9191
9192     // prebuilt conversion function for this field, instead of
9193     // switching every time we're reading a value
9194     if(!this.convert){
9195         var cv, dateFormat = this.dateFormat;
9196         switch(this.type){
9197             case "":
9198             case "auto":
9199             case undefined:
9200                 cv = function(v){ return v; };
9201                 break;
9202             case "string":
9203                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
9204                 break;
9205             case "int":
9206                 cv = function(v){
9207                     return v !== undefined && v !== null && v !== '' ?
9208                            parseInt(String(v).replace(stripRe, ""), 10) : '';
9209                     };
9210                 break;
9211             case "float":
9212                 cv = function(v){
9213                     return v !== undefined && v !== null && v !== '' ?
9214                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
9215                     };
9216                 break;
9217             case "bool":
9218             case "boolean":
9219                 cv = function(v){ return v === true || v === "true" || v == 1; };
9220                 break;
9221             case "date":
9222                 cv = function(v){
9223                     if(!v){
9224                         return '';
9225                     }
9226                     if(v instanceof Date){
9227                         return v;
9228                     }
9229                     if(dateFormat){
9230                         if(dateFormat == "timestamp"){
9231                             return new Date(v*1000);
9232                         }
9233                         return Date.parseDate(v, dateFormat);
9234                     }
9235                     var parsed = Date.parse(v);
9236                     return parsed ? new Date(parsed) : null;
9237                 };
9238              break;
9239             
9240         }
9241         this.convert = cv;
9242     }
9243 };
9244
9245 Roo.data.Field.prototype = {
9246     dateFormat: null,
9247     defaultValue: "",
9248     mapping: null,
9249     sortType : null,
9250     sortDir : "ASC"
9251 };/*
9252  * Based on:
9253  * Ext JS Library 1.1.1
9254  * Copyright(c) 2006-2007, Ext JS, LLC.
9255  *
9256  * Originally Released Under LGPL - original licence link has changed is not relivant.
9257  *
9258  * Fork - LGPL
9259  * <script type="text/javascript">
9260  */
9261  
9262 // Base class for reading structured data from a data source.  This class is intended to be
9263 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
9264
9265 /**
9266  * @class Roo.data.DataReader
9267  * Base class for reading structured data from a data source.  This class is intended to be
9268  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
9269  */
9270
9271 Roo.data.DataReader = function(meta, recordType){
9272     
9273     this.meta = meta;
9274     
9275     this.recordType = recordType instanceof Array ? 
9276         Roo.data.Record.create(recordType) : recordType;
9277 };
9278
9279 Roo.data.DataReader.prototype = {
9280      /**
9281      * Create an empty record
9282      * @param {Object} data (optional) - overlay some values
9283      * @return {Roo.data.Record} record created.
9284      */
9285     newRow :  function(d) {
9286         var da =  {};
9287         this.recordType.prototype.fields.each(function(c) {
9288             switch( c.type) {
9289                 case 'int' : da[c.name] = 0; break;
9290                 case 'date' : da[c.name] = new Date(); break;
9291                 case 'float' : da[c.name] = 0.0; break;
9292                 case 'boolean' : da[c.name] = false; break;
9293                 default : da[c.name] = ""; break;
9294             }
9295             
9296         });
9297         return new this.recordType(Roo.apply(da, d));
9298     }
9299     
9300 };/*
9301  * Based on:
9302  * Ext JS Library 1.1.1
9303  * Copyright(c) 2006-2007, Ext JS, LLC.
9304  *
9305  * Originally Released Under LGPL - original licence link has changed is not relivant.
9306  *
9307  * Fork - LGPL
9308  * <script type="text/javascript">
9309  */
9310
9311 /**
9312  * @class Roo.data.DataProxy
9313  * @extends Roo.data.Observable
9314  * This class is an abstract base class for implementations which provide retrieval of
9315  * unformatted data objects.<br>
9316  * <p>
9317  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
9318  * (of the appropriate type which knows how to parse the data object) to provide a block of
9319  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
9320  * <p>
9321  * Custom implementations must implement the load method as described in
9322  * {@link Roo.data.HttpProxy#load}.
9323  */
9324 Roo.data.DataProxy = function(){
9325     this.addEvents({
9326         /**
9327          * @event beforeload
9328          * Fires before a network request is made to retrieve a data object.
9329          * @param {Object} This DataProxy object.
9330          * @param {Object} params The params parameter to the load function.
9331          */
9332         beforeload : true,
9333         /**
9334          * @event load
9335          * Fires before the load method's callback is called.
9336          * @param {Object} This DataProxy object.
9337          * @param {Object} o The data object.
9338          * @param {Object} arg The callback argument object passed to the load function.
9339          */
9340         load : true,
9341         /**
9342          * @event loadexception
9343          * Fires if an Exception occurs during data retrieval.
9344          * @param {Object} This DataProxy object.
9345          * @param {Object} o The data object.
9346          * @param {Object} arg The callback argument object passed to the load function.
9347          * @param {Object} e The Exception.
9348          */
9349         loadexception : true
9350     });
9351     Roo.data.DataProxy.superclass.constructor.call(this);
9352 };
9353
9354 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
9355
9356     /**
9357      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
9358      */
9359 /*
9360  * Based on:
9361  * Ext JS Library 1.1.1
9362  * Copyright(c) 2006-2007, Ext JS, LLC.
9363  *
9364  * Originally Released Under LGPL - original licence link has changed is not relivant.
9365  *
9366  * Fork - LGPL
9367  * <script type="text/javascript">
9368  */
9369 /**
9370  * @class Roo.data.MemoryProxy
9371  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
9372  * to the Reader when its load method is called.
9373  * @constructor
9374  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
9375  */
9376 Roo.data.MemoryProxy = function(data){
9377     if (data.data) {
9378         data = data.data;
9379     }
9380     Roo.data.MemoryProxy.superclass.constructor.call(this);
9381     this.data = data;
9382 };
9383
9384 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
9385     /**
9386      * Load data from the requested source (in this case an in-memory
9387      * data object passed to the constructor), read the data object into
9388      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9389      * process that block using the passed callback.
9390      * @param {Object} params This parameter is not used by the MemoryProxy class.
9391      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9392      * object into a block of Roo.data.Records.
9393      * @param {Function} callback The function into which to pass the block of Roo.data.records.
9394      * The function must be passed <ul>
9395      * <li>The Record block object</li>
9396      * <li>The "arg" argument from the load function</li>
9397      * <li>A boolean success indicator</li>
9398      * </ul>
9399      * @param {Object} scope The scope in which to call the callback
9400      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9401      */
9402     load : function(params, reader, callback, scope, arg){
9403         params = params || {};
9404         var result;
9405         try {
9406             result = reader.readRecords(this.data);
9407         }catch(e){
9408             this.fireEvent("loadexception", this, arg, null, e);
9409             callback.call(scope, null, arg, false);
9410             return;
9411         }
9412         callback.call(scope, result, arg, true);
9413     },
9414     
9415     // private
9416     update : function(params, records){
9417         
9418     }
9419 });/*
9420  * Based on:
9421  * Ext JS Library 1.1.1
9422  * Copyright(c) 2006-2007, Ext JS, LLC.
9423  *
9424  * Originally Released Under LGPL - original licence link has changed is not relivant.
9425  *
9426  * Fork - LGPL
9427  * <script type="text/javascript">
9428  */
9429 /**
9430  * @class Roo.data.HttpProxy
9431  * @extends Roo.data.DataProxy
9432  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
9433  * configured to reference a certain URL.<br><br>
9434  * <p>
9435  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
9436  * from which the running page was served.<br><br>
9437  * <p>
9438  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
9439  * <p>
9440  * Be aware that to enable the browser to parse an XML document, the server must set
9441  * the Content-Type header in the HTTP response to "text/xml".
9442  * @constructor
9443  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
9444  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
9445  * will be used to make the request.
9446  */
9447 Roo.data.HttpProxy = function(conn){
9448     Roo.data.HttpProxy.superclass.constructor.call(this);
9449     // is conn a conn config or a real conn?
9450     this.conn = conn;
9451     this.useAjax = !conn || !conn.events;
9452   
9453 };
9454
9455 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
9456     // thse are take from connection...
9457     
9458     /**
9459      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
9460      */
9461     /**
9462      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
9463      * extra parameters to each request made by this object. (defaults to undefined)
9464      */
9465     /**
9466      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
9467      *  to each request made by this object. (defaults to undefined)
9468      */
9469     /**
9470      * @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)
9471      */
9472     /**
9473      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
9474      */
9475      /**
9476      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
9477      * @type Boolean
9478      */
9479   
9480
9481     /**
9482      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
9483      * @type Boolean
9484      */
9485     /**
9486      * Return the {@link Roo.data.Connection} object being used by this Proxy.
9487      * @return {Connection} The Connection object. This object may be used to subscribe to events on
9488      * a finer-grained basis than the DataProxy events.
9489      */
9490     getConnection : function(){
9491         return this.useAjax ? Roo.Ajax : this.conn;
9492     },
9493
9494     /**
9495      * Load data from the configured {@link Roo.data.Connection}, read the data object into
9496      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
9497      * process that block using the passed callback.
9498      * @param {Object} params An object containing properties which are to be used as HTTP parameters
9499      * for the request to the remote server.
9500      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9501      * object into a block of Roo.data.Records.
9502      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9503      * The function must be passed <ul>
9504      * <li>The Record block object</li>
9505      * <li>The "arg" argument from the load function</li>
9506      * <li>A boolean success indicator</li>
9507      * </ul>
9508      * @param {Object} scope The scope in which to call the callback
9509      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9510      */
9511     load : function(params, reader, callback, scope, arg){
9512         if(this.fireEvent("beforeload", this, params) !== false){
9513             var  o = {
9514                 params : params || {},
9515                 request: {
9516                     callback : callback,
9517                     scope : scope,
9518                     arg : arg
9519                 },
9520                 reader: reader,
9521                 callback : this.loadResponse,
9522                 scope: this
9523             };
9524             if(this.useAjax){
9525                 Roo.applyIf(o, this.conn);
9526                 if(this.activeRequest){
9527                     Roo.Ajax.abort(this.activeRequest);
9528                 }
9529                 this.activeRequest = Roo.Ajax.request(o);
9530             }else{
9531                 this.conn.request(o);
9532             }
9533         }else{
9534             callback.call(scope||this, null, arg, false);
9535         }
9536     },
9537
9538     // private
9539     loadResponse : function(o, success, response){
9540         delete this.activeRequest;
9541         if(!success){
9542             this.fireEvent("loadexception", this, o, response);
9543             o.request.callback.call(o.request.scope, null, o.request.arg, false);
9544             return;
9545         }
9546         var result;
9547         try {
9548             result = o.reader.read(response);
9549         }catch(e){
9550             this.fireEvent("loadexception", this, o, response, e);
9551             o.request.callback.call(o.request.scope, null, o.request.arg, false);
9552             return;
9553         }
9554         
9555         this.fireEvent("load", this, o, o.request.arg);
9556         o.request.callback.call(o.request.scope, result, o.request.arg, true);
9557     },
9558
9559     // private
9560     update : function(dataSet){
9561
9562     },
9563
9564     // private
9565     updateResponse : function(dataSet){
9566
9567     }
9568 });/*
9569  * Based on:
9570  * Ext JS Library 1.1.1
9571  * Copyright(c) 2006-2007, Ext JS, LLC.
9572  *
9573  * Originally Released Under LGPL - original licence link has changed is not relivant.
9574  *
9575  * Fork - LGPL
9576  * <script type="text/javascript">
9577  */
9578
9579 /**
9580  * @class Roo.data.ScriptTagProxy
9581  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
9582  * other than the originating domain of the running page.<br><br>
9583  * <p>
9584  * <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
9585  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
9586  * <p>
9587  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
9588  * source code that is used as the source inside a &lt;script> tag.<br><br>
9589  * <p>
9590  * In order for the browser to process the returned data, the server must wrap the data object
9591  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
9592  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
9593  * depending on whether the callback name was passed:
9594  * <p>
9595  * <pre><code>
9596 boolean scriptTag = false;
9597 String cb = request.getParameter("callback");
9598 if (cb != null) {
9599     scriptTag = true;
9600     response.setContentType("text/javascript");
9601 } else {
9602     response.setContentType("application/x-json");
9603 }
9604 Writer out = response.getWriter();
9605 if (scriptTag) {
9606     out.write(cb + "(");
9607 }
9608 out.print(dataBlock.toJsonString());
9609 if (scriptTag) {
9610     out.write(");");
9611 }
9612 </pre></code>
9613  *
9614  * @constructor
9615  * @param {Object} config A configuration object.
9616  */
9617 Roo.data.ScriptTagProxy = function(config){
9618     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
9619     Roo.apply(this, config);
9620     this.head = document.getElementsByTagName("head")[0];
9621 };
9622
9623 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
9624
9625 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
9626     /**
9627      * @cfg {String} url The URL from which to request the data object.
9628      */
9629     /**
9630      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
9631      */
9632     timeout : 30000,
9633     /**
9634      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
9635      * the server the name of the callback function set up by the load call to process the returned data object.
9636      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
9637      * javascript output which calls this named function passing the data object as its only parameter.
9638      */
9639     callbackParam : "callback",
9640     /**
9641      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
9642      * name to the request.
9643      */
9644     nocache : true,
9645
9646     /**
9647      * Load data from the configured URL, read the data object into
9648      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9649      * process that block using the passed callback.
9650      * @param {Object} params An object containing properties which are to be used as HTTP parameters
9651      * for the request to the remote server.
9652      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9653      * object into a block of Roo.data.Records.
9654      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9655      * The function must be passed <ul>
9656      * <li>The Record block object</li>
9657      * <li>The "arg" argument from the load function</li>
9658      * <li>A boolean success indicator</li>
9659      * </ul>
9660      * @param {Object} scope The scope in which to call the callback
9661      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9662      */
9663     load : function(params, reader, callback, scope, arg){
9664         if(this.fireEvent("beforeload", this, params) !== false){
9665
9666             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
9667
9668             var url = this.url;
9669             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
9670             if(this.nocache){
9671                 url += "&_dc=" + (new Date().getTime());
9672             }
9673             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
9674             var trans = {
9675                 id : transId,
9676                 cb : "stcCallback"+transId,
9677                 scriptId : "stcScript"+transId,
9678                 params : params,
9679                 arg : arg,
9680                 url : url,
9681                 callback : callback,
9682                 scope : scope,
9683                 reader : reader
9684             };
9685             var conn = this;
9686
9687             window[trans.cb] = function(o){
9688                 conn.handleResponse(o, trans);
9689             };
9690
9691             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
9692
9693             if(this.autoAbort !== false){
9694                 this.abort();
9695             }
9696
9697             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
9698
9699             var script = document.createElement("script");
9700             script.setAttribute("src", url);
9701             script.setAttribute("type", "text/javascript");
9702             script.setAttribute("id", trans.scriptId);
9703             this.head.appendChild(script);
9704
9705             this.trans = trans;
9706         }else{
9707             callback.call(scope||this, null, arg, false);
9708         }
9709     },
9710
9711     // private
9712     isLoading : function(){
9713         return this.trans ? true : false;
9714     },
9715
9716     /**
9717      * Abort the current server request.
9718      */
9719     abort : function(){
9720         if(this.isLoading()){
9721             this.destroyTrans(this.trans);
9722         }
9723     },
9724
9725     // private
9726     destroyTrans : function(trans, isLoaded){
9727         this.head.removeChild(document.getElementById(trans.scriptId));
9728         clearTimeout(trans.timeoutId);
9729         if(isLoaded){
9730             window[trans.cb] = undefined;
9731             try{
9732                 delete window[trans.cb];
9733             }catch(e){}
9734         }else{
9735             // if hasn't been loaded, wait for load to remove it to prevent script error
9736             window[trans.cb] = function(){
9737                 window[trans.cb] = undefined;
9738                 try{
9739                     delete window[trans.cb];
9740                 }catch(e){}
9741             };
9742         }
9743     },
9744
9745     // private
9746     handleResponse : function(o, trans){
9747         this.trans = false;
9748         this.destroyTrans(trans, true);
9749         var result;
9750         try {
9751             result = trans.reader.readRecords(o);
9752         }catch(e){
9753             this.fireEvent("loadexception", this, o, trans.arg, e);
9754             trans.callback.call(trans.scope||window, null, trans.arg, false);
9755             return;
9756         }
9757         this.fireEvent("load", this, o, trans.arg);
9758         trans.callback.call(trans.scope||window, result, trans.arg, true);
9759     },
9760
9761     // private
9762     handleFailure : function(trans){
9763         this.trans = false;
9764         this.destroyTrans(trans, false);
9765         this.fireEvent("loadexception", this, null, trans.arg);
9766         trans.callback.call(trans.scope||window, null, trans.arg, false);
9767     }
9768 });/*
9769  * Based on:
9770  * Ext JS Library 1.1.1
9771  * Copyright(c) 2006-2007, Ext JS, LLC.
9772  *
9773  * Originally Released Under LGPL - original licence link has changed is not relivant.
9774  *
9775  * Fork - LGPL
9776  * <script type="text/javascript">
9777  */
9778
9779 /**
9780  * @class Roo.data.JsonReader
9781  * @extends Roo.data.DataReader
9782  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
9783  * based on mappings in a provided Roo.data.Record constructor.
9784  * 
9785  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
9786  * in the reply previously. 
9787  * 
9788  * <p>
9789  * Example code:
9790  * <pre><code>
9791 var RecordDef = Roo.data.Record.create([
9792     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
9793     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
9794 ]);
9795 var myReader = new Roo.data.JsonReader({
9796     totalProperty: "results",    // The property which contains the total dataset size (optional)
9797     root: "rows",                // The property which contains an Array of row objects
9798     id: "id"                     // The property within each row object that provides an ID for the record (optional)
9799 }, RecordDef);
9800 </code></pre>
9801  * <p>
9802  * This would consume a JSON file like this:
9803  * <pre><code>
9804 { 'results': 2, 'rows': [
9805     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
9806     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
9807 }
9808 </code></pre>
9809  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
9810  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
9811  * paged from the remote server.
9812  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
9813  * @cfg {String} root name of the property which contains the Array of row objects.
9814  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
9815  * @constructor
9816  * Create a new JsonReader
9817  * @param {Object} meta Metadata configuration options
9818  * @param {Object} recordType Either an Array of field definition objects,
9819  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
9820  */
9821 Roo.data.JsonReader = function(meta, recordType){
9822     
9823     meta = meta || {};
9824     // set some defaults:
9825     Roo.applyIf(meta, {
9826         totalProperty: 'total',
9827         successProperty : 'success',
9828         root : 'data',
9829         id : 'id'
9830     });
9831     
9832     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
9833 };
9834 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
9835     
9836     /**
9837      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
9838      * Used by Store query builder to append _requestMeta to params.
9839      * 
9840      */
9841     metaFromRemote : false,
9842     /**
9843      * This method is only used by a DataProxy which has retrieved data from a remote server.
9844      * @param {Object} response The XHR object which contains the JSON data in its responseText.
9845      * @return {Object} data A data block which is used by an Roo.data.Store object as
9846      * a cache of Roo.data.Records.
9847      */
9848     read : function(response){
9849         var json = response.responseText;
9850        
9851         var o = /* eval:var:o */ eval("("+json+")");
9852         if(!o) {
9853             throw {message: "JsonReader.read: Json object not found"};
9854         }
9855         
9856         if(o.metaData){
9857             
9858             delete this.ef;
9859             this.metaFromRemote = true;
9860             this.meta = o.metaData;
9861             this.recordType = Roo.data.Record.create(o.metaData.fields);
9862             this.onMetaChange(this.meta, this.recordType, o);
9863         }
9864         return this.readRecords(o);
9865     },
9866
9867     // private function a store will implement
9868     onMetaChange : function(meta, recordType, o){
9869
9870     },
9871
9872     /**
9873          * @ignore
9874          */
9875     simpleAccess: function(obj, subsc) {
9876         return obj[subsc];
9877     },
9878
9879         /**
9880          * @ignore
9881          */
9882     getJsonAccessor: function(){
9883         var re = /[\[\.]/;
9884         return function(expr) {
9885             try {
9886                 return(re.test(expr))
9887                     ? new Function("obj", "return obj." + expr)
9888                     : function(obj){
9889                         return obj[expr];
9890                     };
9891             } catch(e){}
9892             return Roo.emptyFn;
9893         };
9894     }(),
9895
9896     /**
9897      * Create a data block containing Roo.data.Records from an XML document.
9898      * @param {Object} o An object which contains an Array of row objects in the property specified
9899      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
9900      * which contains the total size of the dataset.
9901      * @return {Object} data A data block which is used by an Roo.data.Store object as
9902      * a cache of Roo.data.Records.
9903      */
9904     readRecords : function(o){
9905         /**
9906          * After any data loads, the raw JSON data is available for further custom processing.
9907          * @type Object
9908          */
9909         this.o = o;
9910         var s = this.meta, Record = this.recordType,
9911             f = Record.prototype.fields, fi = f.items, fl = f.length;
9912
9913 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
9914         if (!this.ef) {
9915             if(s.totalProperty) {
9916                     this.getTotal = this.getJsonAccessor(s.totalProperty);
9917                 }
9918                 if(s.successProperty) {
9919                     this.getSuccess = this.getJsonAccessor(s.successProperty);
9920                 }
9921                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
9922                 if (s.id) {
9923                         var g = this.getJsonAccessor(s.id);
9924                         this.getId = function(rec) {
9925                                 var r = g(rec);
9926                                 return (r === undefined || r === "") ? null : r;
9927                         };
9928                 } else {
9929                         this.getId = function(){return null;};
9930                 }
9931             this.ef = [];
9932             for(var jj = 0; jj < fl; jj++){
9933                 f = fi[jj];
9934                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
9935                 this.ef[jj] = this.getJsonAccessor(map);
9936             }
9937         }
9938
9939         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
9940         if(s.totalProperty){
9941             var vt = parseInt(this.getTotal(o), 10);
9942             if(!isNaN(vt)){
9943                 totalRecords = vt;
9944             }
9945         }
9946         if(s.successProperty){
9947             var vs = this.getSuccess(o);
9948             if(vs === false || vs === 'false'){
9949                 success = false;
9950             }
9951         }
9952         var records = [];
9953             for(var i = 0; i < c; i++){
9954                     var n = root[i];
9955                 var values = {};
9956                 var id = this.getId(n);
9957                 for(var j = 0; j < fl; j++){
9958                     f = fi[j];
9959                 var v = this.ef[j](n);
9960                 if (!f.convert) {
9961                     Roo.log('missing convert for ' + f.name);
9962                     Roo.log(f);
9963                     continue;
9964                 }
9965                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
9966                 }
9967                 var record = new Record(values, id);
9968                 record.json = n;
9969                 records[i] = record;
9970             }
9971             return {
9972             raw : o,
9973                 success : success,
9974                 records : records,
9975                 totalRecords : totalRecords
9976             };
9977     }
9978 });/*
9979  * Based on:
9980  * Ext JS Library 1.1.1
9981  * Copyright(c) 2006-2007, Ext JS, LLC.
9982  *
9983  * Originally Released Under LGPL - original licence link has changed is not relivant.
9984  *
9985  * Fork - LGPL
9986  * <script type="text/javascript">
9987  */
9988
9989 /**
9990  * @class Roo.data.ArrayReader
9991  * @extends Roo.data.DataReader
9992  * Data reader class to create an Array of Roo.data.Record objects from an Array.
9993  * Each element of that Array represents a row of data fields. The
9994  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
9995  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
9996  * <p>
9997  * Example code:.
9998  * <pre><code>
9999 var RecordDef = Roo.data.Record.create([
10000     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
10001     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
10002 ]);
10003 var myReader = new Roo.data.ArrayReader({
10004     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
10005 }, RecordDef);
10006 </code></pre>
10007  * <p>
10008  * This would consume an Array like this:
10009  * <pre><code>
10010 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
10011   </code></pre>
10012  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
10013  * @constructor
10014  * Create a new JsonReader
10015  * @param {Object} meta Metadata configuration options.
10016  * @param {Object} recordType Either an Array of field definition objects
10017  * as specified to {@link Roo.data.Record#create},
10018  * or an {@link Roo.data.Record} object
10019  * created using {@link Roo.data.Record#create}.
10020  */
10021 Roo.data.ArrayReader = function(meta, recordType){
10022     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
10023 };
10024
10025 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
10026     /**
10027      * Create a data block containing Roo.data.Records from an XML document.
10028      * @param {Object} o An Array of row objects which represents the dataset.
10029      * @return {Object} data A data block which is used by an Roo.data.Store object as
10030      * a cache of Roo.data.Records.
10031      */
10032     readRecords : function(o){
10033         var sid = this.meta ? this.meta.id : null;
10034         var recordType = this.recordType, fields = recordType.prototype.fields;
10035         var records = [];
10036         var root = o;
10037             for(var i = 0; i < root.length; i++){
10038                     var n = root[i];
10039                 var values = {};
10040                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
10041                 for(var j = 0, jlen = fields.length; j < jlen; j++){
10042                 var f = fields.items[j];
10043                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
10044                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
10045                 v = f.convert(v);
10046                 values[f.name] = v;
10047             }
10048                 var record = new recordType(values, id);
10049                 record.json = n;
10050                 records[records.length] = record;
10051             }
10052             return {
10053                 records : records,
10054                 totalRecords : records.length
10055             };
10056     }
10057 });/*
10058  * - LGPL
10059  * * 
10060  */
10061
10062 /**
10063  * @class Roo.bootstrap.ComboBox
10064  * @extends Roo.bootstrap.TriggerField
10065  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
10066  * @cfg {Boolean} append (true|false) default false
10067  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
10068  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
10069  * @cfg {Boolean} editNotList allow text type,but not show pull down, default false
10070  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
10071  * @constructor
10072  * Create a new ComboBox.
10073  * @param {Object} config Configuration options
10074  */
10075 Roo.bootstrap.ComboBox = function(config){
10076     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
10077     this.addEvents({
10078         /**
10079          * @event expand
10080          * Fires when the dropdown list is expanded
10081              * @param {Roo.bootstrap.ComboBox} combo This combo box
10082              */
10083         'expand' : true,
10084         /**
10085          * @event collapse
10086          * Fires when the dropdown list is collapsed
10087              * @param {Roo.bootstrap.ComboBox} combo This combo box
10088              */
10089         'collapse' : true,
10090         /**
10091          * @event beforeselect
10092          * Fires before a list item is selected. Return false to cancel the selection.
10093              * @param {Roo.bootstrap.ComboBox} combo This combo box
10094              * @param {Roo.data.Record} record The data record returned from the underlying store
10095              * @param {Number} index The index of the selected item in the dropdown list
10096              */
10097         'beforeselect' : true,
10098         /**
10099          * @event select
10100          * Fires when a list item is selected
10101              * @param {Roo.bootstrap.ComboBox} combo This combo box
10102              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
10103              * @param {Number} index The index of the selected item in the dropdown list
10104              */
10105         'select' : true,
10106         /**
10107          * @event beforequery
10108          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
10109          * The event object passed has these properties:
10110              * @param {Roo.bootstrap.ComboBox} combo This combo box
10111              * @param {String} query The query
10112              * @param {Boolean} forceAll true to force "all" query
10113              * @param {Boolean} cancel true to cancel the query
10114              * @param {Object} e The query event object
10115              */
10116         'beforequery': true,
10117          /**
10118          * @event add
10119          * Fires when the 'add' icon is pressed (add a listener to enable add button)
10120              * @param {Roo.bootstrap.ComboBox} combo This combo box
10121              */
10122         'add' : true,
10123         /**
10124          * @event edit
10125          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
10126              * @param {Roo.bootstrap.ComboBox} combo This combo box
10127              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
10128              */
10129         'edit' : true,
10130         /**
10131          * @event remove
10132          * Fires when the remove value from the combobox array
10133              * @param {Roo.bootstrap.ComboBox} combo This combo box
10134              */
10135         'remove' : true
10136         
10137     });
10138     
10139     this.item = [];
10140     this.tickItems = [];
10141     
10142     this.selectedIndex = -1;
10143     if(this.mode == 'local'){
10144         if(config.queryDelay === undefined){
10145             this.queryDelay = 10;
10146         }
10147         if(config.minChars === undefined){
10148             this.minChars = 0;
10149         }
10150     }
10151 };
10152
10153 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
10154      
10155     /**
10156      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
10157      * rendering into an Roo.Editor, defaults to false)
10158      */
10159     /**
10160      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
10161      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
10162      */
10163     /**
10164      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
10165      */
10166     /**
10167      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
10168      * the dropdown list (defaults to undefined, with no header element)
10169      */
10170
10171      /**
10172      * @cfg {String/Roo.Template} tpl The template to use to render the output
10173      */
10174      
10175      /**
10176      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
10177      */
10178     listWidth: undefined,
10179     /**
10180      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
10181      * mode = 'remote' or 'text' if mode = 'local')
10182      */
10183     displayField: undefined,
10184     /**
10185      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
10186      * mode = 'remote' or 'value' if mode = 'local'). 
10187      * Note: use of a valueField requires the user make a selection
10188      * in order for a value to be mapped.
10189      */
10190     valueField: undefined,
10191     
10192     
10193     /**
10194      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
10195      * field's data value (defaults to the underlying DOM element's name)
10196      */
10197     hiddenName: undefined,
10198     /**
10199      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
10200      */
10201     listClass: '',
10202     /**
10203      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
10204      */
10205     selectedClass: 'active',
10206     
10207     /**
10208      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
10209      */
10210     shadow:'sides',
10211     /**
10212      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
10213      * anchor positions (defaults to 'tl-bl')
10214      */
10215     listAlign: 'tl-bl?',
10216     /**
10217      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
10218      */
10219     maxHeight: 300,
10220     /**
10221      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
10222      * query specified by the allQuery config option (defaults to 'query')
10223      */
10224     triggerAction: 'query',
10225     /**
10226      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
10227      * (defaults to 4, does not apply if editable = false)
10228      */
10229     minChars : 4,
10230     /**
10231      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
10232      * delay (typeAheadDelay) if it matches a known value (defaults to false)
10233      */
10234     typeAhead: false,
10235     /**
10236      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
10237      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
10238      */
10239     queryDelay: 500,
10240     /**
10241      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
10242      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
10243      */
10244     pageSize: 0,
10245     /**
10246      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
10247      * when editable = true (defaults to false)
10248      */
10249     selectOnFocus:false,
10250     /**
10251      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
10252      */
10253     queryParam: 'query',
10254     /**
10255      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
10256      * when mode = 'remote' (defaults to 'Loading...')
10257      */
10258     loadingText: 'Loading...',
10259     /**
10260      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
10261      */
10262     resizable: false,
10263     /**
10264      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
10265      */
10266     handleHeight : 8,
10267     /**
10268      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
10269      * traditional select (defaults to true)
10270      */
10271     editable: true,
10272     /**
10273      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
10274      */
10275     allQuery: '',
10276     /**
10277      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
10278      */
10279     mode: 'remote',
10280     /**
10281      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
10282      * listWidth has a higher value)
10283      */
10284     minListWidth : 70,
10285     /**
10286      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
10287      * allow the user to set arbitrary text into the field (defaults to false)
10288      */
10289     forceSelection:false,
10290     /**
10291      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
10292      * if typeAhead = true (defaults to 250)
10293      */
10294     typeAheadDelay : 250,
10295     /**
10296      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
10297      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
10298      */
10299     valueNotFoundText : undefined,
10300     /**
10301      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
10302      */
10303     blockFocus : false,
10304     
10305     /**
10306      * @cfg {Boolean} disableClear Disable showing of clear button.
10307      */
10308     disableClear : false,
10309     /**
10310      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
10311      */
10312     alwaysQuery : false,
10313     
10314     /**
10315      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
10316      */
10317     multiple : false,
10318     
10319     //private
10320     addicon : false,
10321     editicon: false,
10322     
10323     page: 0,
10324     hasQuery: false,
10325     append: false,
10326     loadNext: false,
10327     autoFocus : true,
10328     tickable : false,
10329     btnPosition : 'right',
10330     editNotList : false,
10331     // element that contains real text value.. (when hidden is used..)
10332     
10333     getAutoCreate : function()
10334     {
10335         var cfg = false;
10336         
10337         /*
10338          *  Normal ComboBox
10339          */
10340         if(!this.tickable){
10341             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
10342             return cfg;
10343         }
10344         
10345         /*
10346          *  ComboBox with tickable selections
10347          */
10348              
10349         var align = this.labelAlign || this.parentLabelAlign();
10350         
10351         cfg = {
10352             cls : 'form-group roo-combobox-tickable' //input-group
10353         };
10354         
10355         
10356         var buttons = {
10357             tag : 'div',
10358             cls : 'tickable-buttons',
10359             cn : [
10360                 {
10361                     tag : 'button',
10362                     type : 'button',
10363                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
10364                     html : 'Edit'
10365                 },
10366                 {
10367                     tag : 'button',
10368                     type : 'button',
10369                     name : 'ok',
10370                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
10371                     html : 'Done'
10372                 },
10373                 {
10374                     tag : 'button',
10375                     type : 'button',
10376                     name : 'cancel',
10377                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
10378                     html : 'Cancel'
10379                 }
10380             ]
10381         };
10382         
10383         var _this = this;
10384         Roo.each(buttons.cn, function(c){
10385             if (_this.size) {
10386                 c.cls += ' btn-' + _this.size;
10387             }
10388
10389             if (_this.disabled) {
10390                 c.disabled = true;
10391             }
10392         });
10393         
10394         var box = {
10395             tag: 'div',
10396             cn: [
10397                 {
10398                     tag: 'input',
10399                     type : 'hidden',
10400                     cls: 'form-hidden-field'
10401                 },
10402                 {
10403                     tag: 'ul',
10404                     cls: 'select2-choices',
10405                     cn:[
10406                         {
10407                             tag: 'li',
10408                             cls: 'select2-search-field',
10409                             cn: [
10410
10411                                 buttons
10412                             ]
10413                         }
10414                     ]
10415                 }
10416             ]
10417         }
10418         
10419         var combobox = {
10420             cls: 'select2-container input-group select2-container-multi',
10421             cn: [
10422                 box,
10423                 {
10424                     tag: 'ul',
10425                     cls: 'typeahead typeahead-long dropdown-menu',
10426                     style: 'display:none; max-height:' + this.maxHeight + 'px;'
10427                 }
10428             ]
10429         };
10430         
10431         if (align ==='left' && this.fieldLabel.length) {
10432             
10433                 Roo.log("left and has label");
10434                 cfg.cn = [
10435                     
10436                     {
10437                         tag: 'label',
10438                         'for' :  id,
10439                         cls : 'control-label col-sm-' + this.labelWidth,
10440                         html : this.fieldLabel
10441                         
10442                     },
10443                     {
10444                         cls : "col-sm-" + (12 - this.labelWidth), 
10445                         cn: [
10446                             combobox
10447                         ]
10448                     }
10449                     
10450                 ];
10451         } else if ( this.fieldLabel.length) {
10452                 Roo.log(" label");
10453                  cfg.cn = [
10454                    
10455                     {
10456                         tag: 'label',
10457                         //cls : 'input-group-addon',
10458                         html : this.fieldLabel
10459                         
10460                     },
10461                     
10462                     combobox
10463                     
10464                 ];
10465
10466         } else {
10467             
10468                 Roo.log(" no label && no align");
10469                 cfg = combobox
10470                      
10471                 
10472         }
10473          
10474         var settings=this;
10475         ['xs','sm','md','lg'].map(function(size){
10476             if (settings[size]) {
10477                 cfg.cls += ' col-' + size + '-' + settings[size];
10478             }
10479         });
10480         
10481         return cfg;
10482         
10483     },
10484     
10485     // private
10486     initEvents: function()
10487     {
10488         
10489         if (!this.store) {
10490             throw "can not find store for combo";
10491         }
10492         this.store = Roo.factory(this.store, Roo.data);
10493         
10494         if(this.tickable){
10495             this.initTickableEvnets();
10496             return;
10497         }
10498         
10499         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
10500         
10501         
10502         if(this.hiddenName){
10503             
10504             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10505             
10506             this.hiddenField.dom.value =
10507                 this.hiddenValue !== undefined ? this.hiddenValue :
10508                 this.value !== undefined ? this.value : '';
10509
10510             // prevent input submission
10511             this.el.dom.removeAttribute('name');
10512             this.hiddenField.dom.setAttribute('name', this.hiddenName);
10513              
10514              
10515         }
10516         //if(Roo.isGecko){
10517         //    this.el.dom.setAttribute('autocomplete', 'off');
10518         //}
10519
10520         var cls = 'x-combo-list';
10521         this.list = this.el.select('ul.dropdown-menu',true).first();
10522
10523         //this.list = new Roo.Layer({
10524         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
10525         //});
10526         
10527         var _this = this;
10528         
10529         (function(){
10530             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
10531             _this.list.setWidth(lw);
10532         }).defer(100);
10533         
10534         this.list.on('mouseover', this.onViewOver, this);
10535         this.list.on('mousemove', this.onViewMove, this);
10536         
10537         this.list.on('scroll', this.onViewScroll, this);
10538         
10539         /*
10540         this.list.swallowEvent('mousewheel');
10541         this.assetHeight = 0;
10542
10543         if(this.title){
10544             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
10545             this.assetHeight += this.header.getHeight();
10546         }
10547
10548         this.innerList = this.list.createChild({cls:cls+'-inner'});
10549         this.innerList.on('mouseover', this.onViewOver, this);
10550         this.innerList.on('mousemove', this.onViewMove, this);
10551         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
10552         
10553         if(this.allowBlank && !this.pageSize && !this.disableClear){
10554             this.footer = this.list.createChild({cls:cls+'-ft'});
10555             this.pageTb = new Roo.Toolbar(this.footer);
10556            
10557         }
10558         if(this.pageSize){
10559             this.footer = this.list.createChild({cls:cls+'-ft'});
10560             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
10561                     {pageSize: this.pageSize});
10562             
10563         }
10564         
10565         if (this.pageTb && this.allowBlank && !this.disableClear) {
10566             var _this = this;
10567             this.pageTb.add(new Roo.Toolbar.Fill(), {
10568                 cls: 'x-btn-icon x-btn-clear',
10569                 text: '&#160;',
10570                 handler: function()
10571                 {
10572                     _this.collapse();
10573                     _this.clearValue();
10574                     _this.onSelect(false, -1);
10575                 }
10576             });
10577         }
10578         if (this.footer) {
10579             this.assetHeight += this.footer.getHeight();
10580         }
10581         */
10582             
10583         if(!this.tpl){
10584             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
10585         }
10586
10587         this.view = new Roo.View(this.el.select('ul.dropdown-menu',true).first(), this.tpl, {
10588             singleSelect:true, store: this.store, selectedClass: this.selectedClass
10589         });
10590         //this.view.wrapEl.setDisplayed(false);
10591         this.view.on('click', this.onViewClick, this);
10592         
10593         
10594         
10595         this.store.on('beforeload', this.onBeforeLoad, this);
10596         this.store.on('load', this.onLoad, this);
10597         this.store.on('loadexception', this.onLoadException, this);
10598         /*
10599         if(this.resizable){
10600             this.resizer = new Roo.Resizable(this.list,  {
10601                pinned:true, handles:'se'
10602             });
10603             this.resizer.on('resize', function(r, w, h){
10604                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
10605                 this.listWidth = w;
10606                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
10607                 this.restrictHeight();
10608             }, this);
10609             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
10610         }
10611         */
10612         if(!this.editable){
10613             this.editable = true;
10614             this.setEditable(false);
10615         }
10616         
10617         /*
10618         
10619         if (typeof(this.events.add.listeners) != 'undefined') {
10620             
10621             this.addicon = this.wrap.createChild(
10622                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
10623        
10624             this.addicon.on('click', function(e) {
10625                 this.fireEvent('add', this);
10626             }, this);
10627         }
10628         if (typeof(this.events.edit.listeners) != 'undefined') {
10629             
10630             this.editicon = this.wrap.createChild(
10631                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
10632             if (this.addicon) {
10633                 this.editicon.setStyle('margin-left', '40px');
10634             }
10635             this.editicon.on('click', function(e) {
10636                 
10637                 // we fire even  if inothing is selected..
10638                 this.fireEvent('edit', this, this.lastData );
10639                 
10640             }, this);
10641         }
10642         */
10643         
10644         this.keyNav = new Roo.KeyNav(this.inputEl(), {
10645             "up" : function(e){
10646                 this.inKeyMode = true;
10647                 this.selectPrev();
10648             },
10649
10650             "down" : function(e){
10651                 if(!this.isExpanded()){
10652                     this.onTriggerClick();
10653                 }else{
10654                     this.inKeyMode = true;
10655                     this.selectNext();
10656                 }
10657             },
10658
10659             "enter" : function(e){
10660 //                this.onViewClick();
10661                 //return true;
10662                 this.collapse();
10663                 
10664                 if(this.fireEvent("specialkey", this, e)){
10665                     this.onViewClick(false);
10666                 }
10667                 
10668                 return true;
10669             },
10670
10671             "esc" : function(e){
10672                 this.collapse();
10673             },
10674
10675             "tab" : function(e){
10676                 this.collapse();
10677                 
10678                 if(this.fireEvent("specialkey", this, e)){
10679                     this.onViewClick(false);
10680                 }
10681                 
10682                 return true;
10683             },
10684
10685             scope : this,
10686
10687             doRelay : function(foo, bar, hname){
10688                 if(hname == 'down' || this.scope.isExpanded()){
10689                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
10690                 }
10691                 return true;
10692             },
10693
10694             forceKeyDown: true
10695         });
10696         
10697         
10698         this.queryDelay = Math.max(this.queryDelay || 10,
10699                 this.mode == 'local' ? 10 : 250);
10700         
10701         
10702         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
10703         
10704         if(this.typeAhead){
10705             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
10706         }
10707         if(this.editable !== false){
10708             this.inputEl().on("keyup", this.onKeyUp, this);
10709         }
10710         if(this.forceSelection){
10711             this.inputEl().on('blur', this.doForce, this);
10712         }
10713         
10714         if(this.multiple){
10715             this.choices = this.el.select('ul.select2-choices', true).first();
10716             this.searchField = this.el.select('ul li.select2-search-field', true).first();
10717         }
10718     },
10719     
10720     initTickableEvnets: function()
10721     {   
10722         if(this.hiddenName){
10723             
10724             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10725             
10726             this.hiddenField.dom.value =
10727                 this.hiddenValue !== undefined ? this.hiddenValue :
10728                 this.value !== undefined ? this.value : '';
10729
10730             // prevent input submission
10731             this.el.dom.removeAttribute('name');
10732             this.hiddenField.dom.setAttribute('name', this.hiddenName);
10733              
10734              
10735         }
10736         
10737         this.list = this.el.select('ul.dropdown-menu',true).first();
10738         
10739         this.choices = this.el.select('ul.select2-choices', true).first();
10740         this.searchField = this.el.select('ul li.select2-search-field', true).first();
10741         if(this.editNotList){
10742             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
10743         }
10744          
10745         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
10746         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
10747         
10748         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
10749         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
10750         
10751         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
10752         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
10753         
10754         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
10755         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
10756         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
10757         
10758         this.okBtn.hide();
10759         this.cancelBtn.hide();
10760         
10761         var _this = this;
10762         
10763         (function(){
10764             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
10765             _this.list.setWidth(lw);
10766         }).defer(100);
10767         
10768         this.list.on('mouseover', this.onViewOver, this);
10769         this.list.on('mousemove', this.onViewMove, this);
10770         
10771         this.list.on('scroll', this.onViewScroll, this);
10772         
10773         if(!this.tpl){
10774             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>';
10775         }
10776
10777         this.view = new Roo.View(this.el.select('ul.dropdown-menu',true).first(), this.tpl, {
10778             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
10779         });
10780         
10781         //this.view.wrapEl.setDisplayed(false);
10782         this.view.on('click', this.onViewClick, this);
10783         
10784         
10785         
10786         this.store.on('beforeload', this.onBeforeLoad, this);
10787         this.store.on('load', this.onLoad, this);
10788         this.store.on('loadexception', this.onLoadException, this);
10789         
10790 //        this.keyNav = new Roo.KeyNav(this.inputEl(), {
10791 //            "up" : function(e){
10792 //                this.inKeyMode = true;
10793 //                this.selectPrev();
10794 //            },
10795 //
10796 //            "down" : function(e){
10797 //                if(!this.isExpanded()){
10798 //                    this.onTriggerClick();
10799 //                }else{
10800 //                    this.inKeyMode = true;
10801 //                    this.selectNext();
10802 //                }
10803 //            },
10804 //
10805 //            "enter" : function(e){
10806 ////                this.onViewClick();
10807 //                //return true;
10808 //                this.collapse();
10809 //                
10810 //                if(this.fireEvent("specialkey", this, e)){
10811 //                    this.onViewClick(false);
10812 //                }
10813 //                
10814 //                return true;
10815 //            },
10816 //
10817 //            "esc" : function(e){
10818 //                this.collapse();
10819 //            },
10820 //
10821 //            "tab" : function(e){
10822 //                this.collapse();
10823 //                
10824 //                if(this.fireEvent("specialkey", this, e)){
10825 //                    this.onViewClick(false);
10826 //                }
10827 //                
10828 //                return true;
10829 //            },
10830 //
10831 //            scope : this,
10832 //
10833 //            doRelay : function(foo, bar, hname){
10834 //                if(hname == 'down' || this.scope.isExpanded()){
10835 //                   return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
10836 //                }
10837 //                return true;
10838 //            },
10839 //
10840 //            forceKeyDown: true
10841 //        });
10842         
10843         
10844         this.queryDelay = Math.max(this.queryDelay || 10,
10845                 this.mode == 'local' ? 10 : 250);
10846         
10847         
10848         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
10849         
10850         if(this.typeAhead){
10851             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
10852         }
10853     },
10854
10855     onDestroy : function(){
10856         if(this.view){
10857             this.view.setStore(null);
10858             this.view.el.removeAllListeners();
10859             this.view.el.remove();
10860             this.view.purgeListeners();
10861         }
10862         if(this.list){
10863             this.list.dom.innerHTML  = '';
10864         }
10865         
10866         if(this.store){
10867             this.store.un('beforeload', this.onBeforeLoad, this);
10868             this.store.un('load', this.onLoad, this);
10869             this.store.un('loadexception', this.onLoadException, this);
10870         }
10871         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
10872     },
10873
10874     // private
10875     fireKey : function(e){
10876         if(e.isNavKeyPress() && !this.list.isVisible()){
10877             this.fireEvent("specialkey", this, e);
10878         }
10879     },
10880
10881     // private
10882     onResize: function(w, h){
10883 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
10884 //        
10885 //        if(typeof w != 'number'){
10886 //            // we do not handle it!?!?
10887 //            return;
10888 //        }
10889 //        var tw = this.trigger.getWidth();
10890 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
10891 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
10892 //        var x = w - tw;
10893 //        this.inputEl().setWidth( this.adjustWidth('input', x));
10894 //            
10895 //        //this.trigger.setStyle('left', x+'px');
10896 //        
10897 //        if(this.list && this.listWidth === undefined){
10898 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
10899 //            this.list.setWidth(lw);
10900 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
10901 //        }
10902         
10903     
10904         
10905     },
10906
10907     /**
10908      * Allow or prevent the user from directly editing the field text.  If false is passed,
10909      * the user will only be able to select from the items defined in the dropdown list.  This method
10910      * is the runtime equivalent of setting the 'editable' config option at config time.
10911      * @param {Boolean} value True to allow the user to directly edit the field text
10912      */
10913     setEditable : function(value){
10914         if(value == this.editable){
10915             return;
10916         }
10917         this.editable = value;
10918         if(!value){
10919             this.inputEl().dom.setAttribute('readOnly', true);
10920             this.inputEl().on('mousedown', this.onTriggerClick,  this);
10921             this.inputEl().addClass('x-combo-noedit');
10922         }else{
10923             this.inputEl().dom.setAttribute('readOnly', false);
10924             this.inputEl().un('mousedown', this.onTriggerClick,  this);
10925             this.inputEl().removeClass('x-combo-noedit');
10926         }
10927     },
10928
10929     // private
10930     
10931     onBeforeLoad : function(combo,opts){
10932         if(!this.hasFocus){
10933             return;
10934         }
10935          if (!opts.add) {
10936             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
10937          }
10938         this.restrictHeight();
10939         this.selectedIndex = -1;
10940     },
10941
10942     // private
10943     onLoad : function(){
10944         
10945         this.hasQuery = false;
10946         
10947         if(!this.hasFocus){
10948             return;
10949         }
10950         
10951         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
10952             this.loading.hide();
10953         }
10954         
10955         if(this.store.getCount() > 0){
10956             this.expand();
10957             this.restrictHeight();
10958             if(this.lastQuery == this.allQuery){
10959                 if(this.editable && !this.tickable){
10960                     this.inputEl().dom.select();
10961                 }
10962                 if(!this.selectByValue(this.value, true) && this.autoFocus){
10963                     this.select(0, true);
10964                 }
10965             }else{
10966                 if(this.autoFocus){
10967                     this.selectNext();
10968                 }
10969                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
10970                     this.taTask.delay(this.typeAheadDelay);
10971                 }
10972             }
10973         }else{
10974             this.onEmptyResults();
10975         }
10976         
10977         //this.el.focus();
10978     },
10979     // private
10980     onLoadException : function()
10981     {
10982         this.hasQuery = false;
10983         
10984         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
10985             this.loading.hide();
10986         }
10987         
10988         this.collapse();
10989         Roo.log(this.store.reader.jsonData);
10990         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
10991             // fixme
10992             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
10993         }
10994         
10995         
10996     },
10997     // private
10998     onTypeAhead : function(){
10999         if(this.store.getCount() > 0){
11000             var r = this.store.getAt(0);
11001             var newValue = r.data[this.displayField];
11002             var len = newValue.length;
11003             var selStart = this.getRawValue().length;
11004             
11005             if(selStart != len){
11006                 this.setRawValue(newValue);
11007                 this.selectText(selStart, newValue.length);
11008             }
11009         }
11010     },
11011
11012     // private
11013     onSelect : function(record, index){
11014         
11015         if(this.fireEvent('beforeselect', this, record, index) !== false){
11016         
11017             this.setFromData(index > -1 ? record.data : false);
11018             
11019             this.collapse();
11020             this.fireEvent('select', this, record, index);
11021         }
11022     },
11023
11024     /**
11025      * Returns the currently selected field value or empty string if no value is set.
11026      * @return {String} value The selected value
11027      */
11028     getValue : function(){
11029         
11030         if(this.multiple){
11031             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
11032         }
11033         
11034         if(this.valueField){
11035             return typeof this.value != 'undefined' ? this.value : '';
11036         }else{
11037             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
11038         }
11039     },
11040
11041     /**
11042      * Clears any text/value currently set in the field
11043      */
11044     clearValue : function(){
11045         if(this.hiddenField){
11046             this.hiddenField.dom.value = '';
11047         }
11048         this.value = '';
11049         this.setRawValue('');
11050         this.lastSelectionText = '';
11051         
11052     },
11053
11054     /**
11055      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
11056      * will be displayed in the field.  If the value does not match the data value of an existing item,
11057      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
11058      * Otherwise the field will be blank (although the value will still be set).
11059      * @param {String} value The value to match
11060      */
11061     setValue : function(v){
11062         if(this.multiple){
11063             this.syncValue();
11064             return;
11065         }
11066         
11067         var text = v;
11068         if(this.valueField){
11069             var r = this.findRecord(this.valueField, v);
11070             if(r){
11071                 text = r.data[this.displayField];
11072             }else if(this.valueNotFoundText !== undefined){
11073                 text = this.valueNotFoundText;
11074             }
11075         }
11076         this.lastSelectionText = text;
11077         if(this.hiddenField){
11078             this.hiddenField.dom.value = v;
11079         }
11080         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
11081         this.value = v;
11082     },
11083     /**
11084      * @property {Object} the last set data for the element
11085      */
11086     
11087     lastData : false,
11088     /**
11089      * Sets the value of the field based on a object which is related to the record format for the store.
11090      * @param {Object} value the value to set as. or false on reset?
11091      */
11092     setFromData : function(o){
11093         
11094         if(this.multiple){
11095             this.addItem(o);
11096             return;
11097         }
11098             
11099         var dv = ''; // display value
11100         var vv = ''; // value value..
11101         this.lastData = o;
11102         if (this.displayField) {
11103             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11104         } else {
11105             // this is an error condition!!!
11106             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
11107         }
11108         
11109         if(this.valueField){
11110             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
11111         }
11112         
11113         if(this.hiddenField){
11114             this.hiddenField.dom.value = vv;
11115             
11116             this.lastSelectionText = dv;
11117             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11118             this.value = vv;
11119             return;
11120         }
11121         // no hidden field.. - we store the value in 'value', but still display
11122         // display field!!!!
11123         this.lastSelectionText = dv;
11124         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11125         this.value = vv;
11126         
11127         
11128     },
11129     // private
11130     reset : function(){
11131         // overridden so that last data is reset..
11132         this.setValue(this.originalValue);
11133         this.clearInvalid();
11134         this.lastData = false;
11135         if (this.view) {
11136             this.view.clearSelections();
11137         }
11138     },
11139     // private
11140     findRecord : function(prop, value){
11141         var record;
11142         if(this.store.getCount() > 0){
11143             this.store.each(function(r){
11144                 if(r.data[prop] == value){
11145                     record = r;
11146                     return false;
11147                 }
11148                 return true;
11149             });
11150         }
11151         return record;
11152     },
11153     
11154     getName: function()
11155     {
11156         // returns hidden if it's set..
11157         if (!this.rendered) {return ''};
11158         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
11159         
11160     },
11161     // private
11162     onViewMove : function(e, t){
11163         this.inKeyMode = false;
11164     },
11165
11166     // private
11167     onViewOver : function(e, t){
11168         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
11169             return;
11170         }
11171         var item = this.view.findItemFromChild(t);
11172         
11173         if(item){
11174             var index = this.view.indexOf(item);
11175             this.select(index, false);
11176         }
11177     },
11178
11179     // private
11180     onViewClick : function(view, doFocus, el, e)
11181     {
11182         var index = this.view.getSelectedIndexes()[0];
11183         
11184         var r = this.store.getAt(index);
11185         
11186         if(this.tickable){
11187             
11188             if(e.getTarget().nodeName.toLowerCase() != 'input'){
11189                 return;
11190             }
11191             
11192             var rm = false;
11193             var _this = this;
11194             
11195             Roo.each(this.tickItems, function(v,k){
11196                 
11197                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
11198                     _this.tickItems.splice(k, 1);
11199                     rm = true;
11200                     return;
11201                 }
11202             })
11203             
11204             if(rm){
11205                 return;
11206             }
11207             
11208             this.tickItems.push(r.data);
11209             return;
11210         }
11211         
11212         if(r){
11213             this.onSelect(r, index);
11214         }
11215         if(doFocus !== false && !this.blockFocus){
11216             this.inputEl().focus();
11217         }
11218     },
11219
11220     // private
11221     restrictHeight : function(){
11222         //this.innerList.dom.style.height = '';
11223         //var inner = this.innerList.dom;
11224         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
11225         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
11226         //this.list.beginUpdate();
11227         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
11228         this.list.alignTo(this.inputEl(), this.listAlign);
11229         //this.list.endUpdate();
11230     },
11231
11232     // private
11233     onEmptyResults : function(){
11234         this.collapse();
11235     },
11236
11237     /**
11238      * Returns true if the dropdown list is expanded, else false.
11239      */
11240     isExpanded : function(){
11241         return this.list.isVisible();
11242     },
11243
11244     /**
11245      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
11246      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11247      * @param {String} value The data value of the item to select
11248      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11249      * selected item if it is not currently in view (defaults to true)
11250      * @return {Boolean} True if the value matched an item in the list, else false
11251      */
11252     selectByValue : function(v, scrollIntoView){
11253         if(v !== undefined && v !== null){
11254             var r = this.findRecord(this.valueField || this.displayField, v);
11255             if(r){
11256                 this.select(this.store.indexOf(r), scrollIntoView);
11257                 return true;
11258             }
11259         }
11260         return false;
11261     },
11262
11263     /**
11264      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
11265      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11266      * @param {Number} index The zero-based index of the list item to select
11267      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11268      * selected item if it is not currently in view (defaults to true)
11269      */
11270     select : function(index, scrollIntoView){
11271         this.selectedIndex = index;
11272         this.view.select(index);
11273         if(scrollIntoView !== false){
11274             var el = this.view.getNode(index);
11275             if(el){
11276                 //this.innerList.scrollChildIntoView(el, false);
11277                 
11278             }
11279         }
11280     },
11281
11282     // private
11283     selectNext : function(){
11284         var ct = this.store.getCount();
11285         if(ct > 0){
11286             if(this.selectedIndex == -1){
11287                 this.select(0);
11288             }else if(this.selectedIndex < ct-1){
11289                 this.select(this.selectedIndex+1);
11290             }
11291         }
11292     },
11293
11294     // private
11295     selectPrev : function(){
11296         var ct = this.store.getCount();
11297         if(ct > 0){
11298             if(this.selectedIndex == -1){
11299                 this.select(0);
11300             }else if(this.selectedIndex != 0){
11301                 this.select(this.selectedIndex-1);
11302             }
11303         }
11304     },
11305
11306     // private
11307     onKeyUp : function(e){
11308         if(this.editable !== false && !e.isSpecialKey()){
11309             this.lastKey = e.getKey();
11310             this.dqTask.delay(this.queryDelay);
11311         }
11312     },
11313
11314     // private
11315     validateBlur : function(){
11316         return !this.list || !this.list.isVisible();   
11317     },
11318
11319     // private
11320     initQuery : function(){
11321         this.doQuery(this.getRawValue());
11322     },
11323
11324     // private
11325     doForce : function(){
11326         if(this.inputEl().dom.value.length > 0){
11327             this.inputEl().dom.value =
11328                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
11329              
11330         }
11331     },
11332
11333     /**
11334      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
11335      * query allowing the query action to be canceled if needed.
11336      * @param {String} query The SQL query to execute
11337      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
11338      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
11339      * saved in the current store (defaults to false)
11340      */
11341     doQuery : function(q, forceAll){
11342         
11343         if(q === undefined || q === null){
11344             q = '';
11345         }
11346         var qe = {
11347             query: q,
11348             forceAll: forceAll,
11349             combo: this,
11350             cancel:false
11351         };
11352         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
11353             return false;
11354         }
11355         q = qe.query;
11356         
11357         forceAll = qe.forceAll;
11358         if(forceAll === true || (q.length >= this.minChars)){
11359             
11360             this.hasQuery = true;
11361             
11362             if(this.lastQuery != q || this.alwaysQuery){
11363                 this.lastQuery = q;
11364                 if(this.mode == 'local'){
11365                     this.selectedIndex = -1;
11366                     if(forceAll){
11367                         this.store.clearFilter();
11368                     }else{
11369                         this.store.filter(this.displayField, q);
11370                     }
11371                     this.onLoad();
11372                 }else{
11373                     this.store.baseParams[this.queryParam] = q;
11374                     
11375                     var options = {params : this.getParams(q)};
11376                     
11377                     if(this.loadNext){
11378                         options.add = true;
11379                         options.params.start = this.page * this.pageSize;
11380                     }
11381                     
11382                     this.store.load(options);
11383                     /*
11384                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
11385                      *  we should expand the list on onLoad
11386                      *  so command out it
11387                      */
11388 //                    this.expand();
11389                 }
11390             }else{
11391                 this.selectedIndex = -1;
11392                 this.onLoad();   
11393             }
11394         }
11395         
11396         this.loadNext = false;
11397     },
11398
11399     // private
11400     getParams : function(q){
11401         var p = {};
11402         //p[this.queryParam] = q;
11403         
11404         if(this.pageSize){
11405             p.start = 0;
11406             p.limit = this.pageSize;
11407         }
11408         return p;
11409     },
11410
11411     /**
11412      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
11413      */
11414     collapse : function(){
11415         if(!this.isExpanded()){
11416             return;
11417         }
11418         
11419         this.hasFocus = false;
11420         
11421         this.list.hide();
11422         
11423         if(this.tickable){
11424             this.okBtn.hide();
11425             this.cancelBtn.hide();
11426             this.trigger.show();
11427         }
11428         
11429         Roo.get(document).un('mousedown', this.collapseIf, this);
11430         Roo.get(document).un('mousewheel', this.collapseIf, this);
11431         if (!this.editable) {
11432             Roo.get(document).un('keydown', this.listKeyPress, this);
11433         }
11434         this.fireEvent('collapse', this);
11435     },
11436
11437     // private
11438     collapseIf : function(e){
11439         var in_combo  = e.within(this.el);
11440         var in_list =  e.within(this.list);
11441         
11442         if (in_combo || in_list) {
11443             //e.stopPropagation();
11444             return;
11445         }
11446         
11447         if(this.tickable){
11448             this.onTickableFooterButtonClick(e, false, false);
11449         }
11450
11451         this.collapse();
11452         
11453     },
11454
11455     /**
11456      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
11457      */
11458     expand : function(){
11459        
11460         if(this.isExpanded() || !this.hasFocus){
11461             return;
11462         }
11463          Roo.log('expand');
11464         this.list.alignTo(this.inputEl(), this.listAlign);
11465         this.list.show();
11466         
11467         if(this.tickable){
11468             
11469             this.tickItems = Roo.apply([], this.item);
11470             
11471             this.okBtn.show();
11472             this.cancelBtn.show();
11473             this.trigger.hide();
11474             
11475         }
11476         
11477         Roo.get(document).on('mousedown', this.collapseIf, this);
11478         Roo.get(document).on('mousewheel', this.collapseIf, this);
11479         if (!this.editable) {
11480             Roo.get(document).on('keydown', this.listKeyPress, this);
11481         }
11482         
11483         this.fireEvent('expand', this);
11484     },
11485
11486     // private
11487     // Implements the default empty TriggerField.onTriggerClick function
11488     onTriggerClick : function(e)
11489     {
11490         Roo.log('trigger click');
11491         
11492         if(this.disabled || this.editNotList){
11493             return;
11494         }
11495         
11496         this.page = 0;
11497         this.loadNext = false;
11498         
11499         if(this.isExpanded()){
11500             this.collapse();
11501             if (!this.blockFocus) {
11502                 this.inputEl().focus();
11503             }
11504             
11505         }else {
11506             this.hasFocus = true;
11507             if(this.triggerAction == 'all') {
11508                 this.doQuery(this.allQuery, true);
11509             } else {
11510                 this.doQuery(this.getRawValue());
11511             }
11512             if (!this.blockFocus) {
11513                 this.inputEl().focus();
11514             }
11515         }
11516     },
11517     
11518     onTickableTriggerClick : function(e)
11519     {
11520         if(this.disabled){
11521             return;
11522         }
11523         
11524         this.page = 0;
11525         this.loadNext = false;
11526         this.hasFocus = true;
11527         
11528         if(this.triggerAction == 'all') {
11529             this.doQuery(this.allQuery, true);
11530         } else {
11531             this.doQuery(this.getRawValue());
11532         }
11533     },
11534     
11535     onSearchFieldClick : function(e)
11536     {
11537         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
11538             return;
11539         }
11540         
11541         this.page = 0;
11542         this.loadNext = false;
11543         this.hasFocus = true;
11544         
11545         if(this.triggerAction == 'all') {
11546             this.doQuery(this.allQuery, true);
11547         } else {
11548             this.doQuery(this.getRawValue());
11549         }
11550     },
11551     
11552     listKeyPress : function(e)
11553     {
11554         //Roo.log('listkeypress');
11555         // scroll to first matching element based on key pres..
11556         if (e.isSpecialKey()) {
11557             return false;
11558         }
11559         var k = String.fromCharCode(e.getKey()).toUpperCase();
11560         //Roo.log(k);
11561         var match  = false;
11562         var csel = this.view.getSelectedNodes();
11563         var cselitem = false;
11564         if (csel.length) {
11565             var ix = this.view.indexOf(csel[0]);
11566             cselitem  = this.store.getAt(ix);
11567             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
11568                 cselitem = false;
11569             }
11570             
11571         }
11572         
11573         this.store.each(function(v) { 
11574             if (cselitem) {
11575                 // start at existing selection.
11576                 if (cselitem.id == v.id) {
11577                     cselitem = false;
11578                 }
11579                 return true;
11580             }
11581                 
11582             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
11583                 match = this.store.indexOf(v);
11584                 return false;
11585             }
11586             return true;
11587         }, this);
11588         
11589         if (match === false) {
11590             return true; // no more action?
11591         }
11592         // scroll to?
11593         this.view.select(match);
11594         var sn = Roo.get(this.view.getSelectedNodes()[0])
11595         //sn.scrollIntoView(sn.dom.parentNode, false);
11596     },
11597     
11598     onViewScroll : function(e, t){
11599         
11600         if(this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
11601             return;
11602         }
11603         
11604         this.hasQuery = true;
11605         
11606         this.loading = this.list.select('.loading', true).first();
11607         
11608         if(this.loading === null){
11609             this.list.createChild({
11610                 tag: 'div',
11611                 cls: 'loading select2-more-results select2-active',
11612                 html: 'Loading more results...'
11613             })
11614             
11615             this.loading = this.list.select('.loading', true).first();
11616             
11617             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
11618             
11619             this.loading.hide();
11620         }
11621         
11622         this.loading.show();
11623         
11624         var _combo = this;
11625         
11626         this.page++;
11627         this.loadNext = true;
11628         
11629         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
11630         
11631         return;
11632     },
11633     
11634     addItem : function(o)
11635     {   
11636         var dv = ''; // display value
11637         
11638         if (this.displayField) {
11639             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11640         } else {
11641             // this is an error condition!!!
11642             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
11643         }
11644         
11645         if(!dv.length){
11646             return;
11647         }
11648         
11649         var choice = this.choices.createChild({
11650             tag: 'li',
11651             cls: 'select2-search-choice',
11652             cn: [
11653                 {
11654                     tag: 'div',
11655                     html: dv
11656                 },
11657                 {
11658                     tag: 'a',
11659                     href: '#',
11660                     cls: 'select2-search-choice-close',
11661                     tabindex: '-1'
11662                 }
11663             ]
11664             
11665         }, this.searchField);
11666         
11667         var close = choice.select('a.select2-search-choice-close', true).first()
11668         
11669         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
11670         
11671         this.item.push(o);
11672         
11673         this.lastData = o;
11674         
11675         this.syncValue();
11676         
11677         this.inputEl().dom.value = '';
11678         
11679     },
11680     
11681     onRemoveItem : function(e, _self, o)
11682     {
11683         e.preventDefault();
11684         var index = this.item.indexOf(o.data) * 1;
11685         
11686         if( index < 0){
11687             Roo.log('not this item?!');
11688             return;
11689         }
11690         
11691         this.item.splice(index, 1);
11692         o.item.remove();
11693         
11694         this.syncValue();
11695         
11696         this.fireEvent('remove', this, e);
11697         
11698     },
11699     
11700     syncValue : function()
11701     {
11702         if(!this.item.length){
11703             this.clearValue();
11704             return;
11705         }
11706             
11707         var value = [];
11708         var _this = this;
11709         Roo.each(this.item, function(i){
11710             if(_this.valueField){
11711                 value.push(i[_this.valueField]);
11712                 return;
11713             }
11714
11715             value.push(i);
11716         });
11717
11718         this.value = value.join(',');
11719
11720         if(this.hiddenField){
11721             this.hiddenField.dom.value = this.value;
11722         }
11723     },
11724     
11725     clearItem : function()
11726     {
11727         if(!this.multiple){
11728             return;
11729         }
11730         
11731         this.item = [];
11732         
11733         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
11734            c.remove();
11735         });
11736         
11737         this.syncValue();
11738     },
11739     
11740     inputEl: function ()
11741     {
11742         if(this.tickable){
11743             return this.searchField;
11744         }
11745         return this.el.select('input.form-control',true).first();
11746     },
11747     
11748     
11749     onTickableFooterButtonClick : function(e, btn, el)
11750     {
11751         e.preventDefault();
11752         
11753         if(btn && btn.name == 'cancel'){
11754             this.tickItems = Roo.apply([], this.item);
11755             this.collapse();
11756             return;
11757         }
11758         
11759         this.clearItem();
11760         
11761         var _this = this;
11762         
11763         Roo.each(this.tickItems, function(o){
11764             _this.addItem(o);
11765         });
11766         
11767         this.collapse();
11768         
11769     }
11770     
11771     
11772
11773     /** 
11774     * @cfg {Boolean} grow 
11775     * @hide 
11776     */
11777     /** 
11778     * @cfg {Number} growMin 
11779     * @hide 
11780     */
11781     /** 
11782     * @cfg {Number} growMax 
11783     * @hide 
11784     */
11785     /**
11786      * @hide
11787      * @method autoSize
11788      */
11789 });
11790 /*
11791  * Based on:
11792  * Ext JS Library 1.1.1
11793  * Copyright(c) 2006-2007, Ext JS, LLC.
11794  *
11795  * Originally Released Under LGPL - original licence link has changed is not relivant.
11796  *
11797  * Fork - LGPL
11798  * <script type="text/javascript">
11799  */
11800
11801 /**
11802  * @class Roo.View
11803  * @extends Roo.util.Observable
11804  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
11805  * This class also supports single and multi selection modes. <br>
11806  * Create a data model bound view:
11807  <pre><code>
11808  var store = new Roo.data.Store(...);
11809
11810  var view = new Roo.View({
11811     el : "my-element",
11812     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
11813  
11814     singleSelect: true,
11815     selectedClass: "ydataview-selected",
11816     store: store
11817  });
11818
11819  // listen for node click?
11820  view.on("click", function(vw, index, node, e){
11821  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
11822  });
11823
11824  // load XML data
11825  dataModel.load("foobar.xml");
11826  </code></pre>
11827  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
11828  * <br><br>
11829  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
11830  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
11831  * 
11832  * Note: old style constructor is still suported (container, template, config)
11833  * 
11834  * @constructor
11835  * Create a new View
11836  * @param {Object} config The config object
11837  * 
11838  */
11839 Roo.View = function(config, depreciated_tpl, depreciated_config){
11840     
11841     this.parent = false;
11842     
11843     if (typeof(depreciated_tpl) == 'undefined') {
11844         // new way.. - universal constructor.
11845         Roo.apply(this, config);
11846         this.el  = Roo.get(this.el);
11847     } else {
11848         // old format..
11849         this.el  = Roo.get(config);
11850         this.tpl = depreciated_tpl;
11851         Roo.apply(this, depreciated_config);
11852     }
11853     this.wrapEl  = this.el.wrap().wrap();
11854     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
11855     
11856     
11857     if(typeof(this.tpl) == "string"){
11858         this.tpl = new Roo.Template(this.tpl);
11859     } else {
11860         // support xtype ctors..
11861         this.tpl = new Roo.factory(this.tpl, Roo);
11862     }
11863     
11864     
11865     this.tpl.compile();
11866     
11867     /** @private */
11868     this.addEvents({
11869         /**
11870          * @event beforeclick
11871          * Fires before a click is processed. Returns false to cancel the default action.
11872          * @param {Roo.View} this
11873          * @param {Number} index The index of the target node
11874          * @param {HTMLElement} node The target node
11875          * @param {Roo.EventObject} e The raw event object
11876          */
11877             "beforeclick" : true,
11878         /**
11879          * @event click
11880          * Fires when a template node is clicked.
11881          * @param {Roo.View} this
11882          * @param {Number} index The index of the target node
11883          * @param {HTMLElement} node The target node
11884          * @param {Roo.EventObject} e The raw event object
11885          */
11886             "click" : true,
11887         /**
11888          * @event dblclick
11889          * Fires when a template node is double clicked.
11890          * @param {Roo.View} this
11891          * @param {Number} index The index of the target node
11892          * @param {HTMLElement} node The target node
11893          * @param {Roo.EventObject} e The raw event object
11894          */
11895             "dblclick" : true,
11896         /**
11897          * @event contextmenu
11898          * Fires when a template node is right clicked.
11899          * @param {Roo.View} this
11900          * @param {Number} index The index of the target node
11901          * @param {HTMLElement} node The target node
11902          * @param {Roo.EventObject} e The raw event object
11903          */
11904             "contextmenu" : true,
11905         /**
11906          * @event selectionchange
11907          * Fires when the selected nodes change.
11908          * @param {Roo.View} this
11909          * @param {Array} selections Array of the selected nodes
11910          */
11911             "selectionchange" : true,
11912     
11913         /**
11914          * @event beforeselect
11915          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
11916          * @param {Roo.View} this
11917          * @param {HTMLElement} node The node to be selected
11918          * @param {Array} selections Array of currently selected nodes
11919          */
11920             "beforeselect" : true,
11921         /**
11922          * @event preparedata
11923          * Fires on every row to render, to allow you to change the data.
11924          * @param {Roo.View} this
11925          * @param {Object} data to be rendered (change this)
11926          */
11927           "preparedata" : true
11928           
11929           
11930         });
11931
11932
11933
11934     this.el.on({
11935         "click": this.onClick,
11936         "dblclick": this.onDblClick,
11937         "contextmenu": this.onContextMenu,
11938         scope:this
11939     });
11940
11941     this.selections = [];
11942     this.nodes = [];
11943     this.cmp = new Roo.CompositeElementLite([]);
11944     if(this.store){
11945         this.store = Roo.factory(this.store, Roo.data);
11946         this.setStore(this.store, true);
11947     }
11948     
11949     if ( this.footer && this.footer.xtype) {
11950            
11951          var fctr = this.wrapEl.appendChild(document.createElement("div"));
11952         
11953         this.footer.dataSource = this.store
11954         this.footer.container = fctr;
11955         this.footer = Roo.factory(this.footer, Roo);
11956         fctr.insertFirst(this.el);
11957         
11958         // this is a bit insane - as the paging toolbar seems to detach the el..
11959 //        dom.parentNode.parentNode.parentNode
11960          // they get detached?
11961     }
11962     
11963     
11964     Roo.View.superclass.constructor.call(this);
11965     
11966     
11967 };
11968
11969 Roo.extend(Roo.View, Roo.util.Observable, {
11970     
11971      /**
11972      * @cfg {Roo.data.Store} store Data store to load data from.
11973      */
11974     store : false,
11975     
11976     /**
11977      * @cfg {String|Roo.Element} el The container element.
11978      */
11979     el : '',
11980     
11981     /**
11982      * @cfg {String|Roo.Template} tpl The template used by this View 
11983      */
11984     tpl : false,
11985     /**
11986      * @cfg {String} dataName the named area of the template to use as the data area
11987      *                          Works with domtemplates roo-name="name"
11988      */
11989     dataName: false,
11990     /**
11991      * @cfg {String} selectedClass The css class to add to selected nodes
11992      */
11993     selectedClass : "x-view-selected",
11994      /**
11995      * @cfg {String} emptyText The empty text to show when nothing is loaded.
11996      */
11997     emptyText : "",
11998     
11999     /**
12000      * @cfg {String} text to display on mask (default Loading)
12001      */
12002     mask : false,
12003     /**
12004      * @cfg {Boolean} multiSelect Allow multiple selection
12005      */
12006     multiSelect : false,
12007     /**
12008      * @cfg {Boolean} singleSelect Allow single selection
12009      */
12010     singleSelect:  false,
12011     
12012     /**
12013      * @cfg {Boolean} toggleSelect - selecting 
12014      */
12015     toggleSelect : false,
12016     
12017     /**
12018      * @cfg {Boolean} tickable - selecting 
12019      */
12020     tickable : false,
12021     
12022     /**
12023      * Returns the element this view is bound to.
12024      * @return {Roo.Element}
12025      */
12026     getEl : function(){
12027         return this.wrapEl;
12028     },
12029     
12030     
12031
12032     /**
12033      * Refreshes the view. - called by datachanged on the store. - do not call directly.
12034      */
12035     refresh : function(){
12036         Roo.log('refresh');
12037         var t = this.tpl;
12038         
12039         // if we are using something like 'domtemplate', then
12040         // the what gets used is:
12041         // t.applySubtemplate(NAME, data, wrapping data..)
12042         // the outer template then get' applied with
12043         //     the store 'extra data'
12044         // and the body get's added to the
12045         //      roo-name="data" node?
12046         //      <span class='roo-tpl-{name}'></span> ?????
12047         
12048         
12049         
12050         this.clearSelections();
12051         this.el.update("");
12052         var html = [];
12053         var records = this.store.getRange();
12054         if(records.length < 1) {
12055             
12056             // is this valid??  = should it render a template??
12057             
12058             this.el.update(this.emptyText);
12059             return;
12060         }
12061         var el = this.el;
12062         if (this.dataName) {
12063             this.el.update(t.apply(this.store.meta)); //????
12064             el = this.el.child('.roo-tpl-' + this.dataName);
12065         }
12066         
12067         for(var i = 0, len = records.length; i < len; i++){
12068             var data = this.prepareData(records[i].data, i, records[i]);
12069             this.fireEvent("preparedata", this, data, i, records[i]);
12070             
12071             var d = Roo.apply({}, data);
12072             
12073             if(this.tickable){
12074                 Roo.apply(d, {'roo-id' : Roo.id()});
12075                 
12076                 var _this = this;
12077             
12078                 Roo.each(this.parent.item, function(item){
12079                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
12080                         return;
12081                     }
12082                     Roo.apply(d, {'roo-data-checked' : 'checked'});
12083                 });
12084             }
12085             
12086             html[html.length] = Roo.util.Format.trim(
12087                 this.dataName ?
12088                     t.applySubtemplate(this.dataName, d, this.store.meta) :
12089                     t.apply(d)
12090             );
12091         }
12092         
12093         
12094         
12095         el.update(html.join(""));
12096         this.nodes = el.dom.childNodes;
12097         this.updateIndexes(0);
12098     },
12099     
12100
12101     /**
12102      * Function to override to reformat the data that is sent to
12103      * the template for each node.
12104      * DEPRICATED - use the preparedata event handler.
12105      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
12106      * a JSON object for an UpdateManager bound view).
12107      */
12108     prepareData : function(data, index, record)
12109     {
12110         this.fireEvent("preparedata", this, data, index, record);
12111         return data;
12112     },
12113
12114     onUpdate : function(ds, record){
12115          Roo.log('on update');   
12116         this.clearSelections();
12117         var index = this.store.indexOf(record);
12118         var n = this.nodes[index];
12119         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
12120         n.parentNode.removeChild(n);
12121         this.updateIndexes(index, index);
12122     },
12123
12124     
12125     
12126 // --------- FIXME     
12127     onAdd : function(ds, records, index)
12128     {
12129         Roo.log(['on Add', ds, records, index] );        
12130         this.clearSelections();
12131         if(this.nodes.length == 0){
12132             this.refresh();
12133             return;
12134         }
12135         var n = this.nodes[index];
12136         for(var i = 0, len = records.length; i < len; i++){
12137             var d = this.prepareData(records[i].data, i, records[i]);
12138             if(n){
12139                 this.tpl.insertBefore(n, d);
12140             }else{
12141                 
12142                 this.tpl.append(this.el, d);
12143             }
12144         }
12145         this.updateIndexes(index);
12146     },
12147
12148     onRemove : function(ds, record, index){
12149         Roo.log('onRemove');
12150         this.clearSelections();
12151         var el = this.dataName  ?
12152             this.el.child('.roo-tpl-' + this.dataName) :
12153             this.el; 
12154         
12155         el.dom.removeChild(this.nodes[index]);
12156         this.updateIndexes(index);
12157     },
12158
12159     /**
12160      * Refresh an individual node.
12161      * @param {Number} index
12162      */
12163     refreshNode : function(index){
12164         this.onUpdate(this.store, this.store.getAt(index));
12165     },
12166
12167     updateIndexes : function(startIndex, endIndex){
12168         var ns = this.nodes;
12169         startIndex = startIndex || 0;
12170         endIndex = endIndex || ns.length - 1;
12171         for(var i = startIndex; i <= endIndex; i++){
12172             ns[i].nodeIndex = i;
12173         }
12174     },
12175
12176     /**
12177      * Changes the data store this view uses and refresh the view.
12178      * @param {Store} store
12179      */
12180     setStore : function(store, initial){
12181         if(!initial && this.store){
12182             this.store.un("datachanged", this.refresh);
12183             this.store.un("add", this.onAdd);
12184             this.store.un("remove", this.onRemove);
12185             this.store.un("update", this.onUpdate);
12186             this.store.un("clear", this.refresh);
12187             this.store.un("beforeload", this.onBeforeLoad);
12188             this.store.un("load", this.onLoad);
12189             this.store.un("loadexception", this.onLoad);
12190         }
12191         if(store){
12192           
12193             store.on("datachanged", this.refresh, this);
12194             store.on("add", this.onAdd, this);
12195             store.on("remove", this.onRemove, this);
12196             store.on("update", this.onUpdate, this);
12197             store.on("clear", this.refresh, this);
12198             store.on("beforeload", this.onBeforeLoad, this);
12199             store.on("load", this.onLoad, this);
12200             store.on("loadexception", this.onLoad, this);
12201         }
12202         
12203         if(store){
12204             this.refresh();
12205         }
12206     },
12207     /**
12208      * onbeforeLoad - masks the loading area.
12209      *
12210      */
12211     onBeforeLoad : function(store,opts)
12212     {
12213          Roo.log('onBeforeLoad');   
12214         if (!opts.add) {
12215             this.el.update("");
12216         }
12217         this.el.mask(this.mask ? this.mask : "Loading" ); 
12218     },
12219     onLoad : function ()
12220     {
12221         this.el.unmask();
12222     },
12223     
12224
12225     /**
12226      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
12227      * @param {HTMLElement} node
12228      * @return {HTMLElement} The template node
12229      */
12230     findItemFromChild : function(node){
12231         var el = this.dataName  ?
12232             this.el.child('.roo-tpl-' + this.dataName,true) :
12233             this.el.dom; 
12234         
12235         if(!node || node.parentNode == el){
12236                     return node;
12237             }
12238             var p = node.parentNode;
12239             while(p && p != el){
12240             if(p.parentNode == el){
12241                 return p;
12242             }
12243             p = p.parentNode;
12244         }
12245             return null;
12246     },
12247
12248     /** @ignore */
12249     onClick : function(e){
12250         var item = this.findItemFromChild(e.getTarget());
12251         if(item){
12252             var index = this.indexOf(item);
12253             if(this.onItemClick(item, index, e) !== false){
12254                 this.fireEvent("click", this, index, item, e);
12255             }
12256         }else{
12257             this.clearSelections();
12258         }
12259     },
12260
12261     /** @ignore */
12262     onContextMenu : function(e){
12263         var item = this.findItemFromChild(e.getTarget());
12264         if(item){
12265             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
12266         }
12267     },
12268
12269     /** @ignore */
12270     onDblClick : function(e){
12271         var item = this.findItemFromChild(e.getTarget());
12272         if(item){
12273             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
12274         }
12275     },
12276
12277     onItemClick : function(item, index, e)
12278     {
12279         if(this.fireEvent("beforeclick", this, index, item, e) === false){
12280             return false;
12281         }
12282         if (this.toggleSelect) {
12283             var m = this.isSelected(item) ? 'unselect' : 'select';
12284             Roo.log(m);
12285             var _t = this;
12286             _t[m](item, true, false);
12287             return true;
12288         }
12289         if(this.multiSelect || this.singleSelect){
12290             if(this.multiSelect && e.shiftKey && this.lastSelection){
12291                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
12292             }else{
12293                 this.select(item, this.multiSelect && e.ctrlKey);
12294                 this.lastSelection = item;
12295             }
12296             
12297             if(!this.tickable){
12298                 e.preventDefault();
12299             }
12300             
12301         }
12302         return true;
12303     },
12304
12305     /**
12306      * Get the number of selected nodes.
12307      * @return {Number}
12308      */
12309     getSelectionCount : function(){
12310         return this.selections.length;
12311     },
12312
12313     /**
12314      * Get the currently selected nodes.
12315      * @return {Array} An array of HTMLElements
12316      */
12317     getSelectedNodes : function(){
12318         return this.selections;
12319     },
12320
12321     /**
12322      * Get the indexes of the selected nodes.
12323      * @return {Array}
12324      */
12325     getSelectedIndexes : function(){
12326         var indexes = [], s = this.selections;
12327         for(var i = 0, len = s.length; i < len; i++){
12328             indexes.push(s[i].nodeIndex);
12329         }
12330         return indexes;
12331     },
12332
12333     /**
12334      * Clear all selections
12335      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
12336      */
12337     clearSelections : function(suppressEvent){
12338         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
12339             this.cmp.elements = this.selections;
12340             this.cmp.removeClass(this.selectedClass);
12341             this.selections = [];
12342             if(!suppressEvent){
12343                 this.fireEvent("selectionchange", this, this.selections);
12344             }
12345         }
12346     },
12347
12348     /**
12349      * Returns true if the passed node is selected
12350      * @param {HTMLElement/Number} node The node or node index
12351      * @return {Boolean}
12352      */
12353     isSelected : function(node){
12354         var s = this.selections;
12355         if(s.length < 1){
12356             return false;
12357         }
12358         node = this.getNode(node);
12359         return s.indexOf(node) !== -1;
12360     },
12361
12362     /**
12363      * Selects nodes.
12364      * @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
12365      * @param {Boolean} keepExisting (optional) true to keep existing selections
12366      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12367      */
12368     select : function(nodeInfo, keepExisting, suppressEvent){
12369         if(nodeInfo instanceof Array){
12370             if(!keepExisting){
12371                 this.clearSelections(true);
12372             }
12373             for(var i = 0, len = nodeInfo.length; i < len; i++){
12374                 this.select(nodeInfo[i], true, true);
12375             }
12376             return;
12377         } 
12378         var node = this.getNode(nodeInfo);
12379         if(!node || this.isSelected(node)){
12380             return; // already selected.
12381         }
12382         if(!keepExisting){
12383             this.clearSelections(true);
12384         }
12385         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
12386             Roo.fly(node).addClass(this.selectedClass);
12387             this.selections.push(node);
12388             if(!suppressEvent){
12389                 this.fireEvent("selectionchange", this, this.selections);
12390             }
12391         }
12392         
12393         
12394     },
12395       /**
12396      * Unselects nodes.
12397      * @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
12398      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
12399      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12400      */
12401     unselect : function(nodeInfo, keepExisting, suppressEvent)
12402     {
12403         if(nodeInfo instanceof Array){
12404             Roo.each(this.selections, function(s) {
12405                 this.unselect(s, nodeInfo);
12406             }, this);
12407             return;
12408         }
12409         var node = this.getNode(nodeInfo);
12410         if(!node || !this.isSelected(node)){
12411             Roo.log("not selected");
12412             return; // not selected.
12413         }
12414         // fireevent???
12415         var ns = [];
12416         Roo.each(this.selections, function(s) {
12417             if (s == node ) {
12418                 Roo.fly(node).removeClass(this.selectedClass);
12419
12420                 return;
12421             }
12422             ns.push(s);
12423         },this);
12424         
12425         this.selections= ns;
12426         this.fireEvent("selectionchange", this, this.selections);
12427     },
12428
12429     /**
12430      * Gets a template node.
12431      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12432      * @return {HTMLElement} The node or null if it wasn't found
12433      */
12434     getNode : function(nodeInfo){
12435         if(typeof nodeInfo == "string"){
12436             return document.getElementById(nodeInfo);
12437         }else if(typeof nodeInfo == "number"){
12438             return this.nodes[nodeInfo];
12439         }
12440         return nodeInfo;
12441     },
12442
12443     /**
12444      * Gets a range template nodes.
12445      * @param {Number} startIndex
12446      * @param {Number} endIndex
12447      * @return {Array} An array of nodes
12448      */
12449     getNodes : function(start, end){
12450         var ns = this.nodes;
12451         start = start || 0;
12452         end = typeof end == "undefined" ? ns.length - 1 : end;
12453         var nodes = [];
12454         if(start <= end){
12455             for(var i = start; i <= end; i++){
12456                 nodes.push(ns[i]);
12457             }
12458         } else{
12459             for(var i = start; i >= end; i--){
12460                 nodes.push(ns[i]);
12461             }
12462         }
12463         return nodes;
12464     },
12465
12466     /**
12467      * Finds the index of the passed node
12468      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12469      * @return {Number} The index of the node or -1
12470      */
12471     indexOf : function(node){
12472         node = this.getNode(node);
12473         if(typeof node.nodeIndex == "number"){
12474             return node.nodeIndex;
12475         }
12476         var ns = this.nodes;
12477         for(var i = 0, len = ns.length; i < len; i++){
12478             if(ns[i] == node){
12479                 return i;
12480             }
12481         }
12482         return -1;
12483     }
12484 });
12485 /*
12486  * - LGPL
12487  *
12488  * based on jquery fullcalendar
12489  * 
12490  */
12491
12492 Roo.bootstrap = Roo.bootstrap || {};
12493 /**
12494  * @class Roo.bootstrap.Calendar
12495  * @extends Roo.bootstrap.Component
12496  * Bootstrap Calendar class
12497  * @cfg {Boolean} loadMask (true|false) default false
12498  * @cfg {Object} header generate the user specific header of the calendar, default false
12499
12500  * @constructor
12501  * Create a new Container
12502  * @param {Object} config The config object
12503  */
12504
12505
12506
12507 Roo.bootstrap.Calendar = function(config){
12508     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
12509      this.addEvents({
12510         /**
12511              * @event select
12512              * Fires when a date is selected
12513              * @param {DatePicker} this
12514              * @param {Date} date The selected date
12515              */
12516         'select': true,
12517         /**
12518              * @event monthchange
12519              * Fires when the displayed month changes 
12520              * @param {DatePicker} this
12521              * @param {Date} date The selected month
12522              */
12523         'monthchange': true,
12524         /**
12525              * @event evententer
12526              * Fires when mouse over an event
12527              * @param {Calendar} this
12528              * @param {event} Event
12529              */
12530         'evententer': true,
12531         /**
12532              * @event eventleave
12533              * Fires when the mouse leaves an
12534              * @param {Calendar} this
12535              * @param {event}
12536              */
12537         'eventleave': true,
12538         /**
12539              * @event eventclick
12540              * Fires when the mouse click an
12541              * @param {Calendar} this
12542              * @param {event}
12543              */
12544         'eventclick': true
12545         
12546     });
12547
12548 };
12549
12550 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
12551     
12552      /**
12553      * @cfg {Number} startDay
12554      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
12555      */
12556     startDay : 0,
12557     
12558     loadMask : false,
12559     
12560     header : false,
12561       
12562     getAutoCreate : function(){
12563         
12564         
12565         var fc_button = function(name, corner, style, content ) {
12566             return Roo.apply({},{
12567                 tag : 'span',
12568                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
12569                          (corner.length ?
12570                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
12571                             ''
12572                         ),
12573                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
12574                 unselectable: 'on'
12575             });
12576         };
12577         
12578         var header = {};
12579         
12580         if(!this.header){
12581             header = {
12582                 tag : 'table',
12583                 cls : 'fc-header',
12584                 style : 'width:100%',
12585                 cn : [
12586                     {
12587                         tag: 'tr',
12588                         cn : [
12589                             {
12590                                 tag : 'td',
12591                                 cls : 'fc-header-left',
12592                                 cn : [
12593                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
12594                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
12595                                     { tag: 'span', cls: 'fc-header-space' },
12596                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
12597
12598
12599                                 ]
12600                             },
12601
12602                             {
12603                                 tag : 'td',
12604                                 cls : 'fc-header-center',
12605                                 cn : [
12606                                     {
12607                                         tag: 'span',
12608                                         cls: 'fc-header-title',
12609                                         cn : {
12610                                             tag: 'H2',
12611                                             html : 'month / year'
12612                                         }
12613                                     }
12614
12615                                 ]
12616                             },
12617                             {
12618                                 tag : 'td',
12619                                 cls : 'fc-header-right',
12620                                 cn : [
12621                               /*      fc_button('month', 'left', '', 'month' ),
12622                                     fc_button('week', '', '', 'week' ),
12623                                     fc_button('day', 'right', '', 'day' )
12624                                 */    
12625
12626                                 ]
12627                             }
12628
12629                         ]
12630                     }
12631                 ]
12632             };
12633         }
12634         
12635         header = this.header;
12636         
12637        
12638         var cal_heads = function() {
12639             var ret = [];
12640             // fixme - handle this.
12641             
12642             for (var i =0; i < Date.dayNames.length; i++) {
12643                 var d = Date.dayNames[i];
12644                 ret.push({
12645                     tag: 'th',
12646                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
12647                     html : d.substring(0,3)
12648                 });
12649                 
12650             }
12651             ret[0].cls += ' fc-first';
12652             ret[6].cls += ' fc-last';
12653             return ret;
12654         };
12655         var cal_cell = function(n) {
12656             return  {
12657                 tag: 'td',
12658                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
12659                 cn : [
12660                     {
12661                         cn : [
12662                             {
12663                                 cls: 'fc-day-number',
12664                                 html: 'D'
12665                             },
12666                             {
12667                                 cls: 'fc-day-content',
12668                              
12669                                 cn : [
12670                                      {
12671                                         style: 'position: relative;' // height: 17px;
12672                                     }
12673                                 ]
12674                             }
12675                             
12676                             
12677                         ]
12678                     }
12679                 ]
12680                 
12681             }
12682         };
12683         var cal_rows = function() {
12684             
12685             var ret = []
12686             for (var r = 0; r < 6; r++) {
12687                 var row= {
12688                     tag : 'tr',
12689                     cls : 'fc-week',
12690                     cn : []
12691                 };
12692                 
12693                 for (var i =0; i < Date.dayNames.length; i++) {
12694                     var d = Date.dayNames[i];
12695                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
12696
12697                 }
12698                 row.cn[0].cls+=' fc-first';
12699                 row.cn[0].cn[0].style = 'min-height:90px';
12700                 row.cn[6].cls+=' fc-last';
12701                 ret.push(row);
12702                 
12703             }
12704             ret[0].cls += ' fc-first';
12705             ret[4].cls += ' fc-prev-last';
12706             ret[5].cls += ' fc-last';
12707             return ret;
12708             
12709         };
12710         
12711         var cal_table = {
12712             tag: 'table',
12713             cls: 'fc-border-separate',
12714             style : 'width:100%',
12715             cellspacing  : 0,
12716             cn : [
12717                 { 
12718                     tag: 'thead',
12719                     cn : [
12720                         { 
12721                             tag: 'tr',
12722                             cls : 'fc-first fc-last',
12723                             cn : cal_heads()
12724                         }
12725                     ]
12726                 },
12727                 { 
12728                     tag: 'tbody',
12729                     cn : cal_rows()
12730                 }
12731                   
12732             ]
12733         };
12734          
12735          var cfg = {
12736             cls : 'fc fc-ltr',
12737             cn : [
12738                 header,
12739                 {
12740                     cls : 'fc-content',
12741                     style : "position: relative;",
12742                     cn : [
12743                         {
12744                             cls : 'fc-view fc-view-month fc-grid',
12745                             style : 'position: relative',
12746                             unselectable : 'on',
12747                             cn : [
12748                                 {
12749                                     cls : 'fc-event-container',
12750                                     style : 'position:absolute;z-index:8;top:0;left:0;'
12751                                 },
12752                                 cal_table
12753                             ]
12754                         }
12755                     ]
12756     
12757                 }
12758            ] 
12759             
12760         };
12761         
12762          
12763         
12764         return cfg;
12765     },
12766     
12767     
12768     initEvents : function()
12769     {
12770         if(!this.store){
12771             throw "can not find store for calendar";
12772         }
12773         
12774         var mark = {
12775             tag: "div",
12776             cls:"x-dlg-mask",
12777             style: "text-align:center",
12778             cn: [
12779                 {
12780                     tag: "div",
12781                     style: "background-color:white;width:50%;margin:250 auto",
12782                     cn: [
12783                         {
12784                             tag: "img",
12785                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
12786                         },
12787                         {
12788                             tag: "span",
12789                             html: "Loading"
12790                         }
12791                         
12792                     ]
12793                 }
12794             ]
12795         }
12796         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
12797         
12798         var size = this.el.select('.fc-content', true).first().getSize();
12799         this.maskEl.setSize(size.width, size.height);
12800         this.maskEl.enableDisplayMode("block");
12801         if(!this.loadMask){
12802             this.maskEl.hide();
12803         }
12804         
12805         this.store = Roo.factory(this.store, Roo.data);
12806         this.store.on('load', this.onLoad, this);
12807         this.store.on('beforeload', this.onBeforeLoad, this);
12808         
12809         this.resize();
12810         
12811         this.cells = this.el.select('.fc-day',true);
12812         //Roo.log(this.cells);
12813         this.textNodes = this.el.query('.fc-day-number');
12814         this.cells.addClassOnOver('fc-state-hover');
12815         
12816         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
12817         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
12818         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
12819         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
12820         
12821         this.on('monthchange', this.onMonthChange, this);
12822         
12823         this.update(new Date().clearTime());
12824     },
12825     
12826     resize : function() {
12827         var sz  = this.el.getSize();
12828         
12829         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
12830         this.el.select('.fc-day-content div',true).setHeight(34);
12831     },
12832     
12833     
12834     // private
12835     showPrevMonth : function(e){
12836         this.update(this.activeDate.add("mo", -1));
12837     },
12838     showToday : function(e){
12839         this.update(new Date().clearTime());
12840     },
12841     // private
12842     showNextMonth : function(e){
12843         this.update(this.activeDate.add("mo", 1));
12844     },
12845
12846     // private
12847     showPrevYear : function(){
12848         this.update(this.activeDate.add("y", -1));
12849     },
12850
12851     // private
12852     showNextYear : function(){
12853         this.update(this.activeDate.add("y", 1));
12854     },
12855
12856     
12857    // private
12858     update : function(date)
12859     {
12860         var vd = this.activeDate;
12861         this.activeDate = date;
12862 //        if(vd && this.el){
12863 //            var t = date.getTime();
12864 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
12865 //                Roo.log('using add remove');
12866 //                
12867 //                this.fireEvent('monthchange', this, date);
12868 //                
12869 //                this.cells.removeClass("fc-state-highlight");
12870 //                this.cells.each(function(c){
12871 //                   if(c.dateValue == t){
12872 //                       c.addClass("fc-state-highlight");
12873 //                       setTimeout(function(){
12874 //                            try{c.dom.firstChild.focus();}catch(e){}
12875 //                       }, 50);
12876 //                       return false;
12877 //                   }
12878 //                   return true;
12879 //                });
12880 //                return;
12881 //            }
12882 //        }
12883         
12884         var days = date.getDaysInMonth();
12885         
12886         var firstOfMonth = date.getFirstDateOfMonth();
12887         var startingPos = firstOfMonth.getDay()-this.startDay;
12888         
12889         if(startingPos < this.startDay){
12890             startingPos += 7;
12891         }
12892         
12893         var pm = date.add(Date.MONTH, -1);
12894         var prevStart = pm.getDaysInMonth()-startingPos;
12895 //        
12896         this.cells = this.el.select('.fc-day',true);
12897         this.textNodes = this.el.query('.fc-day-number');
12898         this.cells.addClassOnOver('fc-state-hover');
12899         
12900         var cells = this.cells.elements;
12901         var textEls = this.textNodes;
12902         
12903         Roo.each(cells, function(cell){
12904             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
12905         });
12906         
12907         days += startingPos;
12908
12909         // convert everything to numbers so it's fast
12910         var day = 86400000;
12911         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
12912         //Roo.log(d);
12913         //Roo.log(pm);
12914         //Roo.log(prevStart);
12915         
12916         var today = new Date().clearTime().getTime();
12917         var sel = date.clearTime().getTime();
12918         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
12919         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
12920         var ddMatch = this.disabledDatesRE;
12921         var ddText = this.disabledDatesText;
12922         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
12923         var ddaysText = this.disabledDaysText;
12924         var format = this.format;
12925         
12926         var setCellClass = function(cal, cell){
12927             cell.row = 0;
12928             cell.events = [];
12929             cell.more = [];
12930             //Roo.log('set Cell Class');
12931             cell.title = "";
12932             var t = d.getTime();
12933             
12934             //Roo.log(d);
12935             
12936             cell.dateValue = t;
12937             if(t == today){
12938                 cell.className += " fc-today";
12939                 cell.className += " fc-state-highlight";
12940                 cell.title = cal.todayText;
12941             }
12942             if(t == sel){
12943                 // disable highlight in other month..
12944                 //cell.className += " fc-state-highlight";
12945                 
12946             }
12947             // disabling
12948             if(t < min) {
12949                 cell.className = " fc-state-disabled";
12950                 cell.title = cal.minText;
12951                 return;
12952             }
12953             if(t > max) {
12954                 cell.className = " fc-state-disabled";
12955                 cell.title = cal.maxText;
12956                 return;
12957             }
12958             if(ddays){
12959                 if(ddays.indexOf(d.getDay()) != -1){
12960                     cell.title = ddaysText;
12961                     cell.className = " fc-state-disabled";
12962                 }
12963             }
12964             if(ddMatch && format){
12965                 var fvalue = d.dateFormat(format);
12966                 if(ddMatch.test(fvalue)){
12967                     cell.title = ddText.replace("%0", fvalue);
12968                     cell.className = " fc-state-disabled";
12969                 }
12970             }
12971             
12972             if (!cell.initialClassName) {
12973                 cell.initialClassName = cell.dom.className;
12974             }
12975             
12976             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
12977         };
12978
12979         var i = 0;
12980         
12981         for(; i < startingPos; i++) {
12982             textEls[i].innerHTML = (++prevStart);
12983             d.setDate(d.getDate()+1);
12984             
12985             cells[i].className = "fc-past fc-other-month";
12986             setCellClass(this, cells[i]);
12987         }
12988         
12989         var intDay = 0;
12990         
12991         for(; i < days; i++){
12992             intDay = i - startingPos + 1;
12993             textEls[i].innerHTML = (intDay);
12994             d.setDate(d.getDate()+1);
12995             
12996             cells[i].className = ''; // "x-date-active";
12997             setCellClass(this, cells[i]);
12998         }
12999         var extraDays = 0;
13000         
13001         for(; i < 42; i++) {
13002             textEls[i].innerHTML = (++extraDays);
13003             d.setDate(d.getDate()+1);
13004             
13005             cells[i].className = "fc-future fc-other-month";
13006             setCellClass(this, cells[i]);
13007         }
13008         
13009         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
13010         
13011         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
13012         
13013         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
13014         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
13015         
13016         if(totalRows != 6){
13017             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
13018             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
13019         }
13020         
13021         this.fireEvent('monthchange', this, date);
13022         
13023         
13024         /*
13025         if(!this.internalRender){
13026             var main = this.el.dom.firstChild;
13027             var w = main.offsetWidth;
13028             this.el.setWidth(w + this.el.getBorderWidth("lr"));
13029             Roo.fly(main).setWidth(w);
13030             this.internalRender = true;
13031             // opera does not respect the auto grow header center column
13032             // then, after it gets a width opera refuses to recalculate
13033             // without a second pass
13034             if(Roo.isOpera && !this.secondPass){
13035                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
13036                 this.secondPass = true;
13037                 this.update.defer(10, this, [date]);
13038             }
13039         }
13040         */
13041         
13042     },
13043     
13044     findCell : function(dt) {
13045         dt = dt.clearTime().getTime();
13046         var ret = false;
13047         this.cells.each(function(c){
13048             //Roo.log("check " +c.dateValue + '?=' + dt);
13049             if(c.dateValue == dt){
13050                 ret = c;
13051                 return false;
13052             }
13053             return true;
13054         });
13055         
13056         return ret;
13057     },
13058     
13059     findCells : function(ev) {
13060         var s = ev.start.clone().clearTime().getTime();
13061        // Roo.log(s);
13062         var e= ev.end.clone().clearTime().getTime();
13063        // Roo.log(e);
13064         var ret = [];
13065         this.cells.each(function(c){
13066              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
13067             
13068             if(c.dateValue > e){
13069                 return ;
13070             }
13071             if(c.dateValue < s){
13072                 return ;
13073             }
13074             ret.push(c);
13075         });
13076         
13077         return ret;    
13078     },
13079     
13080 //    findBestRow: function(cells)
13081 //    {
13082 //        var ret = 0;
13083 //        
13084 //        for (var i =0 ; i < cells.length;i++) {
13085 //            ret  = Math.max(cells[i].rows || 0,ret);
13086 //        }
13087 //        return ret;
13088 //        
13089 //    },
13090     
13091     
13092     addItem : function(ev)
13093     {
13094         // look for vertical location slot in
13095         var cells = this.findCells(ev);
13096         
13097 //        ev.row = this.findBestRow(cells);
13098         
13099         // work out the location.
13100         
13101         var crow = false;
13102         var rows = [];
13103         for(var i =0; i < cells.length; i++) {
13104             
13105             cells[i].row = cells[0].row;
13106             
13107             if(i == 0){
13108                 cells[i].row = cells[i].row + 1;
13109             }
13110             
13111             if (!crow) {
13112                 crow = {
13113                     start : cells[i],
13114                     end :  cells[i]
13115                 };
13116                 continue;
13117             }
13118             if (crow.start.getY() == cells[i].getY()) {
13119                 // on same row.
13120                 crow.end = cells[i];
13121                 continue;
13122             }
13123             // different row.
13124             rows.push(crow);
13125             crow = {
13126                 start: cells[i],
13127                 end : cells[i]
13128             };
13129             
13130         }
13131         
13132         rows.push(crow);
13133         ev.els = [];
13134         ev.rows = rows;
13135         ev.cells = cells;
13136         
13137         cells[0].events.push(ev);
13138         
13139         this.calevents.push(ev);
13140     },
13141     
13142     clearEvents: function() {
13143         
13144         if(!this.calevents){
13145             return;
13146         }
13147         
13148         Roo.each(this.cells.elements, function(c){
13149             c.row = 0;
13150             c.events = [];
13151             c.more = [];
13152         });
13153         
13154         Roo.each(this.calevents, function(e) {
13155             Roo.each(e.els, function(el) {
13156                 el.un('mouseenter' ,this.onEventEnter, this);
13157                 el.un('mouseleave' ,this.onEventLeave, this);
13158                 el.remove();
13159             },this);
13160         },this);
13161         
13162         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
13163             e.remove();
13164         });
13165         
13166     },
13167     
13168     renderEvents: function()
13169     {   
13170         var _this = this;
13171         
13172         this.cells.each(function(c) {
13173             
13174             if(c.row < 5){
13175                 return;
13176             }
13177             
13178             var ev = c.events;
13179             
13180             var r = 4;
13181             if(c.row != c.events.length){
13182                 r = 4 - (4 - (c.row - c.events.length));
13183             }
13184             
13185             c.events = ev.slice(0, r);
13186             c.more = ev.slice(r);
13187             
13188             if(c.more.length && c.more.length == 1){
13189                 c.events.push(c.more.pop());
13190             }
13191             
13192             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
13193             
13194         });
13195             
13196         this.cells.each(function(c) {
13197             
13198             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
13199             
13200             
13201             for (var e = 0; e < c.events.length; e++){
13202                 var ev = c.events[e];
13203                 var rows = ev.rows;
13204                 
13205                 for(var i = 0; i < rows.length; i++) {
13206                 
13207                     // how many rows should it span..
13208
13209                     var  cfg = {
13210                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
13211                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
13212
13213                         unselectable : "on",
13214                         cn : [
13215                             {
13216                                 cls: 'fc-event-inner',
13217                                 cn : [
13218     //                                {
13219     //                                  tag:'span',
13220     //                                  cls: 'fc-event-time',
13221     //                                  html : cells.length > 1 ? '' : ev.time
13222     //                                },
13223                                     {
13224                                       tag:'span',
13225                                       cls: 'fc-event-title',
13226                                       html : String.format('{0}', ev.title)
13227                                     }
13228
13229
13230                                 ]
13231                             },
13232                             {
13233                                 cls: 'ui-resizable-handle ui-resizable-e',
13234                                 html : '&nbsp;&nbsp;&nbsp'
13235                             }
13236
13237                         ]
13238                     };
13239
13240                     if (i == 0) {
13241                         cfg.cls += ' fc-event-start';
13242                     }
13243                     if ((i+1) == rows.length) {
13244                         cfg.cls += ' fc-event-end';
13245                     }
13246
13247                     var ctr = _this.el.select('.fc-event-container',true).first();
13248                     var cg = ctr.createChild(cfg);
13249
13250                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
13251                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
13252
13253                     var r = (c.more.length) ? 1 : 0;
13254                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
13255                     cg.setWidth(ebox.right - sbox.x -2);
13256
13257                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
13258                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
13259                     cg.on('click', _this.onEventClick, _this, ev);
13260
13261                     ev.els.push(cg);
13262                     
13263                 }
13264                 
13265             }
13266             
13267             
13268             if(c.more.length){
13269                 var  cfg = {
13270                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
13271                     style : 'position: absolute',
13272                     unselectable : "on",
13273                     cn : [
13274                         {
13275                             cls: 'fc-event-inner',
13276                             cn : [
13277                                 {
13278                                   tag:'span',
13279                                   cls: 'fc-event-title',
13280                                   html : 'More'
13281                                 }
13282
13283
13284                             ]
13285                         },
13286                         {
13287                             cls: 'ui-resizable-handle ui-resizable-e',
13288                             html : '&nbsp;&nbsp;&nbsp'
13289                         }
13290
13291                     ]
13292                 };
13293
13294                 var ctr = _this.el.select('.fc-event-container',true).first();
13295                 var cg = ctr.createChild(cfg);
13296
13297                 var sbox = c.select('.fc-day-content',true).first().getBox();
13298                 var ebox = c.select('.fc-day-content',true).first().getBox();
13299                 //Roo.log(cg);
13300                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
13301                 cg.setWidth(ebox.right - sbox.x -2);
13302
13303                 cg.on('click', _this.onMoreEventClick, _this, c.more);
13304                 
13305             }
13306             
13307         });
13308         
13309         
13310         
13311     },
13312     
13313     onEventEnter: function (e, el,event,d) {
13314         this.fireEvent('evententer', this, el, event);
13315     },
13316     
13317     onEventLeave: function (e, el,event,d) {
13318         this.fireEvent('eventleave', this, el, event);
13319     },
13320     
13321     onEventClick: function (e, el,event,d) {
13322         this.fireEvent('eventclick', this, el, event);
13323     },
13324     
13325     onMonthChange: function () {
13326         this.store.load();
13327     },
13328     
13329     onMoreEventClick: function(e, el, more)
13330     {
13331         var _this = this;
13332         
13333         this.calpopover.placement = 'right';
13334         this.calpopover.setTitle('More');
13335         
13336         this.calpopover.setContent('');
13337         
13338         var ctr = this.calpopover.el.select('.popover-content', true).first();
13339         
13340         Roo.each(more, function(m){
13341             var cfg = {
13342                 cls : 'fc-event-hori fc-event-draggable',
13343                 html : m.title
13344             }
13345             var cg = ctr.createChild(cfg);
13346             
13347             cg.on('click', _this.onEventClick, _this, m);
13348         });
13349         
13350         this.calpopover.show(el);
13351         
13352         
13353     },
13354     
13355     onLoad: function () 
13356     {   
13357         this.calevents = [];
13358         var cal = this;
13359         
13360         if(this.store.getCount() > 0){
13361             this.store.data.each(function(d){
13362                cal.addItem({
13363                     id : d.data.id,
13364                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
13365                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
13366                     time : d.data.start_time,
13367                     title : d.data.title,
13368                     description : d.data.description,
13369                     venue : d.data.venue
13370                 });
13371             });
13372         }
13373         
13374         this.renderEvents();
13375         
13376         if(this.calevents.length && this.loadMask){
13377             this.maskEl.hide();
13378         }
13379     },
13380     
13381     onBeforeLoad: function()
13382     {
13383         this.clearEvents();
13384         if(this.loadMask){
13385             this.maskEl.show();
13386         }
13387     }
13388 });
13389
13390  
13391  /*
13392  * - LGPL
13393  *
13394  * element
13395  * 
13396  */
13397
13398 /**
13399  * @class Roo.bootstrap.Popover
13400  * @extends Roo.bootstrap.Component
13401  * Bootstrap Popover class
13402  * @cfg {String} html contents of the popover   (or false to use children..)
13403  * @cfg {String} title of popover (or false to hide)
13404  * @cfg {String} placement how it is placed
13405  * @cfg {String} trigger click || hover (or false to trigger manually)
13406  * @cfg {String} over what (parent or false to trigger manually.)
13407  * 
13408  * @constructor
13409  * Create a new Popover
13410  * @param {Object} config The config object
13411  */
13412
13413 Roo.bootstrap.Popover = function(config){
13414     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
13415 };
13416
13417 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
13418     
13419     title: 'Fill in a title',
13420     html: false,
13421     
13422     placement : 'right',
13423     trigger : 'hover', // hover
13424     
13425     over: 'parent',
13426     
13427     can_build_overlaid : false,
13428     
13429     getChildContainer : function()
13430     {
13431         return this.el.select('.popover-content',true).first();
13432     },
13433     
13434     getAutoCreate : function(){
13435          Roo.log('make popover?');
13436         var cfg = {
13437            cls : 'popover roo-dynamic',
13438            style: 'display:block',
13439            cn : [
13440                 {
13441                     cls : 'arrow'
13442                 },
13443                 {
13444                     cls : 'popover-inner',
13445                     cn : [
13446                         {
13447                             tag: 'h3',
13448                             cls: 'popover-title',
13449                             html : this.title
13450                         },
13451                         {
13452                             cls : 'popover-content',
13453                             html : this.html
13454                         }
13455                     ]
13456                     
13457                 }
13458            ]
13459         };
13460         
13461         return cfg;
13462     },
13463     setTitle: function(str)
13464     {
13465         this.el.select('.popover-title',true).first().dom.innerHTML = str;
13466     },
13467     setContent: function(str)
13468     {
13469         this.el.select('.popover-content',true).first().dom.innerHTML = str;
13470     },
13471     // as it get's added to the bottom of the page.
13472     onRender : function(ct, position)
13473     {
13474         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
13475         if(!this.el){
13476             var cfg = Roo.apply({},  this.getAutoCreate());
13477             cfg.id = Roo.id();
13478             
13479             if (this.cls) {
13480                 cfg.cls += ' ' + this.cls;
13481             }
13482             if (this.style) {
13483                 cfg.style = this.style;
13484             }
13485             Roo.log("adding to ")
13486             this.el = Roo.get(document.body).createChild(cfg, position);
13487             Roo.log(this.el);
13488         }
13489         this.initEvents();
13490     },
13491     
13492     initEvents : function()
13493     {
13494         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
13495         this.el.enableDisplayMode('block');
13496         this.el.hide();
13497         if (this.over === false) {
13498             return; 
13499         }
13500         if (this.triggers === false) {
13501             return;
13502         }
13503         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13504         var triggers = this.trigger ? this.trigger.split(' ') : [];
13505         Roo.each(triggers, function(trigger) {
13506         
13507             if (trigger == 'click') {
13508                 on_el.on('click', this.toggle, this);
13509             } else if (trigger != 'manual') {
13510                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
13511                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
13512       
13513                 on_el.on(eventIn  ,this.enter, this);
13514                 on_el.on(eventOut, this.leave, this);
13515             }
13516         }, this);
13517         
13518     },
13519     
13520     
13521     // private
13522     timeout : null,
13523     hoverState : null,
13524     
13525     toggle : function () {
13526         this.hoverState == 'in' ? this.leave() : this.enter();
13527     },
13528     
13529     enter : function () {
13530        
13531     
13532         clearTimeout(this.timeout);
13533     
13534         this.hoverState = 'in'
13535     
13536         if (!this.delay || !this.delay.show) {
13537             this.show();
13538             return 
13539         }
13540         var _t = this;
13541         this.timeout = setTimeout(function () {
13542             if (_t.hoverState == 'in') {
13543                 _t.show();
13544             }
13545         }, this.delay.show)
13546     },
13547     leave : function() {
13548         clearTimeout(this.timeout);
13549     
13550         this.hoverState = 'out'
13551     
13552         if (!this.delay || !this.delay.hide) {
13553             this.hide();
13554             return 
13555         }
13556         var _t = this;
13557         this.timeout = setTimeout(function () {
13558             if (_t.hoverState == 'out') {
13559                 _t.hide();
13560             }
13561         }, this.delay.hide)
13562     },
13563     
13564     show : function (on_el)
13565     {
13566         if (!on_el) {
13567             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13568         }
13569         // set content.
13570         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
13571         if (this.html !== false) {
13572             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
13573         }
13574         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
13575         if (!this.title.length) {
13576             this.el.select('.popover-title',true).hide();
13577         }
13578         
13579         var placement = typeof this.placement == 'function' ?
13580             this.placement.call(this, this.el, on_el) :
13581             this.placement;
13582             
13583         var autoToken = /\s?auto?\s?/i;
13584         var autoPlace = autoToken.test(placement);
13585         if (autoPlace) {
13586             placement = placement.replace(autoToken, '') || 'top';
13587         }
13588         
13589         //this.el.detach()
13590         //this.el.setXY([0,0]);
13591         this.el.show();
13592         this.el.dom.style.display='block';
13593         this.el.addClass(placement);
13594         
13595         //this.el.appendTo(on_el);
13596         
13597         var p = this.getPosition();
13598         var box = this.el.getBox();
13599         
13600         if (autoPlace) {
13601             // fixme..
13602         }
13603         var align = Roo.bootstrap.Popover.alignment[placement]
13604         this.el.alignTo(on_el, align[0],align[1]);
13605         //var arrow = this.el.select('.arrow',true).first();
13606         //arrow.set(align[2], 
13607         
13608         this.el.addClass('in');
13609         this.hoverState = null;
13610         
13611         if (this.el.hasClass('fade')) {
13612             // fade it?
13613         }
13614         
13615     },
13616     hide : function()
13617     {
13618         this.el.setXY([0,0]);
13619         this.el.removeClass('in');
13620         this.el.hide();
13621         
13622     }
13623     
13624 });
13625
13626 Roo.bootstrap.Popover.alignment = {
13627     'left' : ['r-l', [-10,0], 'right'],
13628     'right' : ['l-r', [10,0], 'left'],
13629     'bottom' : ['t-b', [0,10], 'top'],
13630     'top' : [ 'b-t', [0,-10], 'bottom']
13631 };
13632
13633  /*
13634  * - LGPL
13635  *
13636  * Progress
13637  * 
13638  */
13639
13640 /**
13641  * @class Roo.bootstrap.Progress
13642  * @extends Roo.bootstrap.Component
13643  * Bootstrap Progress class
13644  * @cfg {Boolean} striped striped of the progress bar
13645  * @cfg {Boolean} active animated of the progress bar
13646  * 
13647  * 
13648  * @constructor
13649  * Create a new Progress
13650  * @param {Object} config The config object
13651  */
13652
13653 Roo.bootstrap.Progress = function(config){
13654     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
13655 };
13656
13657 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
13658     
13659     striped : false,
13660     active: false,
13661     
13662     getAutoCreate : function(){
13663         var cfg = {
13664             tag: 'div',
13665             cls: 'progress'
13666         };
13667         
13668         
13669         if(this.striped){
13670             cfg.cls += ' progress-striped';
13671         }
13672       
13673         if(this.active){
13674             cfg.cls += ' active';
13675         }
13676         
13677         
13678         return cfg;
13679     }
13680    
13681 });
13682
13683  
13684
13685  /*
13686  * - LGPL
13687  *
13688  * ProgressBar
13689  * 
13690  */
13691
13692 /**
13693  * @class Roo.bootstrap.ProgressBar
13694  * @extends Roo.bootstrap.Component
13695  * Bootstrap ProgressBar class
13696  * @cfg {Number} aria_valuenow aria-value now
13697  * @cfg {Number} aria_valuemin aria-value min
13698  * @cfg {Number} aria_valuemax aria-value max
13699  * @cfg {String} label label for the progress bar
13700  * @cfg {String} panel (success | info | warning | danger )
13701  * @cfg {String} role role of the progress bar
13702  * @cfg {String} sr_only text
13703  * 
13704  * 
13705  * @constructor
13706  * Create a new ProgressBar
13707  * @param {Object} config The config object
13708  */
13709
13710 Roo.bootstrap.ProgressBar = function(config){
13711     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
13712 };
13713
13714 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
13715     
13716     aria_valuenow : 0,
13717     aria_valuemin : 0,
13718     aria_valuemax : 100,
13719     label : false,
13720     panel : false,
13721     role : false,
13722     sr_only: false,
13723     
13724     getAutoCreate : function()
13725     {
13726         
13727         var cfg = {
13728             tag: 'div',
13729             cls: 'progress-bar',
13730             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
13731         };
13732         
13733         if(this.sr_only){
13734             cfg.cn = {
13735                 tag: 'span',
13736                 cls: 'sr-only',
13737                 html: this.sr_only
13738             }
13739         }
13740         
13741         if(this.role){
13742             cfg.role = this.role;
13743         }
13744         
13745         if(this.aria_valuenow){
13746             cfg['aria-valuenow'] = this.aria_valuenow;
13747         }
13748         
13749         if(this.aria_valuemin){
13750             cfg['aria-valuemin'] = this.aria_valuemin;
13751         }
13752         
13753         if(this.aria_valuemax){
13754             cfg['aria-valuemax'] = this.aria_valuemax;
13755         }
13756         
13757         if(this.label && !this.sr_only){
13758             cfg.html = this.label;
13759         }
13760         
13761         if(this.panel){
13762             cfg.cls += ' progress-bar-' + this.panel;
13763         }
13764         
13765         return cfg;
13766     },
13767     
13768     update : function(aria_valuenow)
13769     {
13770         this.aria_valuenow = aria_valuenow;
13771         
13772         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
13773     }
13774    
13775 });
13776
13777  
13778
13779  /*
13780  * - LGPL
13781  *
13782  * column
13783  * 
13784  */
13785
13786 /**
13787  * @class Roo.bootstrap.TabGroup
13788  * @extends Roo.bootstrap.Column
13789  * Bootstrap Column class
13790  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
13791  * @cfg {Boolean} carousel true to make the group behave like a carousel
13792  * 
13793  * @constructor
13794  * Create a new TabGroup
13795  * @param {Object} config The config object
13796  */
13797
13798 Roo.bootstrap.TabGroup = function(config){
13799     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
13800     if (!this.navId) {
13801         this.navId = Roo.id();
13802     }
13803     this.tabs = [];
13804     Roo.bootstrap.TabGroup.register(this);
13805     
13806 };
13807
13808 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
13809     
13810     carousel : false,
13811      
13812     getAutoCreate : function()
13813     {
13814         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
13815         
13816         cfg.cls += ' tab-content';
13817         
13818         if (this.carousel) {
13819             cfg.cls += ' carousel slide';
13820             cfg.cn = [{
13821                cls : 'carousel-inner'
13822             }]
13823         }
13824         
13825         
13826         return cfg;
13827     },
13828     getChildContainer : function()
13829     {
13830         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
13831     },
13832     
13833     /**
13834     * register a Navigation item
13835     * @param {Roo.bootstrap.NavItem} the navitem to add
13836     */
13837     register : function(item)
13838     {
13839         this.tabs.push( item);
13840         item.navId = this.navId; // not really needed..
13841     
13842     },
13843     
13844     getActivePanel : function()
13845     {
13846         var r = false;
13847         Roo.each(this.tabs, function(t) {
13848             if (t.active) {
13849                 r = t;
13850                 return false;
13851             }
13852             return null;
13853         });
13854         return r;
13855         
13856     },
13857     getPanelByName : function(n)
13858     {
13859         var r = false;
13860         Roo.each(this.tabs, function(t) {
13861             if (t.tabId == n) {
13862                 r = t;
13863                 return false;
13864             }
13865             return null;
13866         });
13867         return r;
13868     },
13869     indexOfPanel : function(p)
13870     {
13871         var r = false;
13872         Roo.each(this.tabs, function(t,i) {
13873             if (t.tabId == p.tabId) {
13874                 r = i;
13875                 return false;
13876             }
13877             return null;
13878         });
13879         return r;
13880     },
13881     /**
13882      * show a specific panel
13883      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
13884      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
13885      */
13886     showPanel : function (pan)
13887     {
13888         
13889         
13890         
13891         if (typeof(pan) == 'number') {
13892             pan = this.tabs[pan];
13893         }
13894         if (typeof(pan) == 'string') {
13895             pan = this.getPanelByName(pan);
13896         }
13897         if (pan.tabId == this.getActivePanel().tabId) {
13898             return true;
13899         }
13900         var cur = this.getActivePanel();
13901         
13902         if (false === cur.fireEvent('beforedeactivate')) {
13903             return false;
13904         }
13905         
13906         
13907         
13908         if (this.carousel) {
13909             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
13910             var lr = dir == 'next' ? 'left' : 'right';
13911             pan.el.addClass(dir); // or prev
13912             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
13913             cur.el.addClass(lr); // or right
13914             pan.el.addClass(lr);
13915             cur.el.on('transitionend', function() {
13916                 Roo.log("trans end?");
13917                 
13918                 pan.el.removeClass([lr,dir]);
13919                 pan.setActive(true);
13920                 
13921                 cur.el.removeClass([lr]);
13922                 cur.setActive(false);
13923                 
13924                 
13925             }, this, { single:  true } );
13926             return true;
13927         }
13928         
13929         cur.setActive(false);
13930         pan.setActive(true);
13931         return true;
13932         
13933     },
13934     showPanelNext : function()
13935     {
13936         var i = this.indexOfPanel(this.getActivePanel());
13937         if (i > this.tabs.length) {
13938             return;
13939         }
13940         this.showPanel(this.tabs[i+1]);
13941     },
13942     showPanelPrev : function()
13943     {
13944         var i = this.indexOfPanel(this.getActivePanel());
13945         if (i  < 1) {
13946             return;
13947         }
13948         this.showPanel(this.tabs[i-1]);
13949     }
13950     
13951     
13952   
13953 });
13954
13955  
13956
13957  
13958  
13959 Roo.apply(Roo.bootstrap.TabGroup, {
13960     
13961     groups: {},
13962      /**
13963     * register a Navigation Group
13964     * @param {Roo.bootstrap.NavGroup} the navgroup to add
13965     */
13966     register : function(navgrp)
13967     {
13968         this.groups[navgrp.navId] = navgrp;
13969         
13970     },
13971     /**
13972     * fetch a Navigation Group based on the navigation ID
13973     * if one does not exist , it will get created.
13974     * @param {string} the navgroup to add
13975     * @returns {Roo.bootstrap.NavGroup} the navgroup 
13976     */
13977     get: function(navId) {
13978         if (typeof(this.groups[navId]) == 'undefined') {
13979             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
13980         }
13981         return this.groups[navId] ;
13982     }
13983     
13984     
13985     
13986 });
13987
13988  /*
13989  * - LGPL
13990  *
13991  * TabPanel
13992  * 
13993  */
13994
13995 /**
13996  * @class Roo.bootstrap.TabPanel
13997  * @extends Roo.bootstrap.Component
13998  * Bootstrap TabPanel class
13999  * @cfg {Boolean} active panel active
14000  * @cfg {String} html panel content
14001  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
14002  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
14003  * 
14004  * 
14005  * @constructor
14006  * Create a new TabPanel
14007  * @param {Object} config The config object
14008  */
14009
14010 Roo.bootstrap.TabPanel = function(config){
14011     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
14012     this.addEvents({
14013         /**
14014              * @event changed
14015              * Fires when the active status changes
14016              * @param {Roo.bootstrap.TabPanel} this
14017              * @param {Boolean} state the new state
14018             
14019          */
14020         'changed': true,
14021         /**
14022              * @event beforedeactivate
14023              * Fires before a tab is de-activated - can be used to do validation on a form.
14024              * @param {Roo.bootstrap.TabPanel} this
14025              * @return {Boolean} false if there is an error
14026             
14027          */
14028         'beforedeactivate': true
14029      });
14030     
14031     this.tabId = this.tabId || Roo.id();
14032   
14033 };
14034
14035 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
14036     
14037     active: false,
14038     html: false,
14039     tabId: false,
14040     navId : false,
14041     
14042     getAutoCreate : function(){
14043         var cfg = {
14044             tag: 'div',
14045             // item is needed for carousel - not sure if it has any effect otherwise
14046             cls: 'tab-pane item',
14047             html: this.html || ''
14048         };
14049         
14050         if(this.active){
14051             cfg.cls += ' active';
14052         }
14053         
14054         if(this.tabId){
14055             cfg.tabId = this.tabId;
14056         }
14057         
14058         
14059         return cfg;
14060     },
14061     
14062     initEvents:  function()
14063     {
14064         Roo.log('-------- init events on tab panel ---------');
14065         
14066         var p = this.parent();
14067         this.navId = this.navId || p.navId;
14068         
14069         if (typeof(this.navId) != 'undefined') {
14070             // not really needed.. but just in case.. parent should be a NavGroup.
14071             var tg = Roo.bootstrap.TabGroup.get(this.navId);
14072             Roo.log(['register', tg, this]);
14073             tg.register(this);
14074         }
14075     },
14076     
14077     
14078     onRender : function(ct, position)
14079     {
14080        // Roo.log("Call onRender: " + this.xtype);
14081         
14082         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
14083         
14084         
14085         
14086         
14087         
14088     },
14089     
14090     setActive: function(state)
14091     {
14092         Roo.log("panel - set active " + this.tabId + "=" + state);
14093         
14094         this.active = state;
14095         if (!state) {
14096             this.el.removeClass('active');
14097             
14098         } else  if (!this.el.hasClass('active')) {
14099             this.el.addClass('active');
14100         }
14101         this.fireEvent('changed', this, state);
14102     }
14103     
14104     
14105 });
14106  
14107
14108  
14109
14110  /*
14111  * - LGPL
14112  *
14113  * DateField
14114  * 
14115  */
14116
14117 /**
14118  * @class Roo.bootstrap.DateField
14119  * @extends Roo.bootstrap.Input
14120  * Bootstrap DateField class
14121  * @cfg {Number} weekStart default 0
14122  * @cfg {Number} weekStart default 0
14123  * @cfg {Number} viewMode default empty, (months|years)
14124  * @cfg {Number} minViewMode default empty, (months|years)
14125  * @cfg {Number} startDate default -Infinity
14126  * @cfg {Number} endDate default Infinity
14127  * @cfg {Boolean} todayHighlight default false
14128  * @cfg {Boolean} todayBtn default false
14129  * @cfg {Boolean} calendarWeeks default false
14130  * @cfg {Object} daysOfWeekDisabled default empty
14131  * 
14132  * @cfg {Boolean} keyboardNavigation default true
14133  * @cfg {String} language default en
14134  * 
14135  * @constructor
14136  * Create a new DateField
14137  * @param {Object} config The config object
14138  */
14139
14140 Roo.bootstrap.DateField = function(config){
14141     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
14142      this.addEvents({
14143             /**
14144              * @event show
14145              * Fires when this field show.
14146              * @param {Roo.bootstrap.DateField} this
14147              * @param {Mixed} date The date value
14148              */
14149             show : true,
14150             /**
14151              * @event show
14152              * Fires when this field hide.
14153              * @param {Roo.bootstrap.DateField} this
14154              * @param {Mixed} date The date value
14155              */
14156             hide : true,
14157             /**
14158              * @event select
14159              * Fires when select a date.
14160              * @param {Roo.bootstrap.DateField} this
14161              * @param {Mixed} date The date value
14162              */
14163             select : true
14164         });
14165 };
14166
14167 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
14168     
14169     /**
14170      * @cfg {String} format
14171      * The default date format string which can be overriden for localization support.  The format must be
14172      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
14173      */
14174     format : "m/d/y",
14175     /**
14176      * @cfg {String} altFormats
14177      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
14178      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
14179      */
14180     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
14181     
14182     weekStart : 0,
14183     
14184     viewMode : '',
14185     
14186     minViewMode : '',
14187     
14188     todayHighlight : false,
14189     
14190     todayBtn: false,
14191     
14192     language: 'en',
14193     
14194     keyboardNavigation: true,
14195     
14196     calendarWeeks: false,
14197     
14198     startDate: -Infinity,
14199     
14200     endDate: Infinity,
14201     
14202     daysOfWeekDisabled: [],
14203     
14204     _events: [],
14205     
14206     UTCDate: function()
14207     {
14208         return new Date(Date.UTC.apply(Date, arguments));
14209     },
14210     
14211     UTCToday: function()
14212     {
14213         var today = new Date();
14214         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
14215     },
14216     
14217     getDate: function() {
14218             var d = this.getUTCDate();
14219             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
14220     },
14221     
14222     getUTCDate: function() {
14223             return this.date;
14224     },
14225     
14226     setDate: function(d) {
14227             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
14228     },
14229     
14230     setUTCDate: function(d) {
14231             this.date = d;
14232             this.setValue(this.formatDate(this.date));
14233     },
14234         
14235     onRender: function(ct, position)
14236     {
14237         
14238         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
14239         
14240         this.language = this.language || 'en';
14241         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
14242         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
14243         
14244         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
14245         this.format = this.format || 'm/d/y';
14246         this.isInline = false;
14247         this.isInput = true;
14248         this.component = this.el.select('.add-on', true).first() || false;
14249         this.component = (this.component && this.component.length === 0) ? false : this.component;
14250         this.hasInput = this.component && this.inputEL().length;
14251         
14252         if (typeof(this.minViewMode === 'string')) {
14253             switch (this.minViewMode) {
14254                 case 'months':
14255                     this.minViewMode = 1;
14256                     break;
14257                 case 'years':
14258                     this.minViewMode = 2;
14259                     break;
14260                 default:
14261                     this.minViewMode = 0;
14262                     break;
14263             }
14264         }
14265         
14266         if (typeof(this.viewMode === 'string')) {
14267             switch (this.viewMode) {
14268                 case 'months':
14269                     this.viewMode = 1;
14270                     break;
14271                 case 'years':
14272                     this.viewMode = 2;
14273                     break;
14274                 default:
14275                     this.viewMode = 0;
14276                     break;
14277             }
14278         }
14279                 
14280         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
14281         
14282         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14283         
14284         this.picker().on('mousedown', this.onMousedown, this);
14285         this.picker().on('click', this.onClick, this);
14286         
14287         this.picker().addClass('datepicker-dropdown');
14288         
14289         this.startViewMode = this.viewMode;
14290         
14291         
14292         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
14293             if(!this.calendarWeeks){
14294                 v.remove();
14295                 return;
14296             };
14297             
14298             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
14299             v.attr('colspan', function(i, val){
14300                 return parseInt(val) + 1;
14301             });
14302         })
14303                         
14304         
14305         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
14306         
14307         this.setStartDate(this.startDate);
14308         this.setEndDate(this.endDate);
14309         
14310         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
14311         
14312         this.fillDow();
14313         this.fillMonths();
14314         this.update();
14315         this.showMode();
14316         
14317         if(this.isInline) {
14318             this.show();
14319         }
14320     },
14321     
14322     picker : function()
14323     {
14324         return this.el.select('.datepicker', true).first();
14325     },
14326     
14327     fillDow: function()
14328     {
14329         var dowCnt = this.weekStart;
14330         
14331         var dow = {
14332             tag: 'tr',
14333             cn: [
14334                 
14335             ]
14336         };
14337         
14338         if(this.calendarWeeks){
14339             dow.cn.push({
14340                 tag: 'th',
14341                 cls: 'cw',
14342                 html: '&nbsp;'
14343             })
14344         }
14345         
14346         while (dowCnt < this.weekStart + 7) {
14347             dow.cn.push({
14348                 tag: 'th',
14349                 cls: 'dow',
14350                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
14351             });
14352         }
14353         
14354         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
14355     },
14356     
14357     fillMonths: function()
14358     {    
14359         var i = 0
14360         var months = this.picker().select('>.datepicker-months td', true).first();
14361         
14362         months.dom.innerHTML = '';
14363         
14364         while (i < 12) {
14365             var month = {
14366                 tag: 'span',
14367                 cls: 'month',
14368                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
14369             }
14370             
14371             months.createChild(month);
14372         }
14373         
14374     },
14375     
14376     update: function()
14377     {
14378         
14379         this.date = (typeof(this.date) === 'undefined') ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
14380         
14381         if (this.date < this.startDate) {
14382             this.viewDate = new Date(this.startDate);
14383         } else if (this.date > this.endDate) {
14384             this.viewDate = new Date(this.endDate);
14385         } else {
14386             this.viewDate = new Date(this.date);
14387         }
14388         
14389         this.fill();
14390     },
14391     
14392     fill: function() 
14393     {
14394         var d = new Date(this.viewDate),
14395                 year = d.getUTCFullYear(),
14396                 month = d.getUTCMonth(),
14397                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
14398                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
14399                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
14400                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
14401                 currentDate = this.date && this.date.valueOf(),
14402                 today = this.UTCToday();
14403         
14404         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
14405         
14406 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
14407         
14408 //        this.picker.select('>tfoot th.today').
14409 //                                              .text(dates[this.language].today)
14410 //                                              .toggle(this.todayBtn !== false);
14411     
14412         this.updateNavArrows();
14413         this.fillMonths();
14414                                                 
14415         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
14416         
14417         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
14418          
14419         prevMonth.setUTCDate(day);
14420         
14421         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
14422         
14423         var nextMonth = new Date(prevMonth);
14424         
14425         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
14426         
14427         nextMonth = nextMonth.valueOf();
14428         
14429         var fillMonths = false;
14430         
14431         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
14432         
14433         while(prevMonth.valueOf() < nextMonth) {
14434             var clsName = '';
14435             
14436             if (prevMonth.getUTCDay() === this.weekStart) {
14437                 if(fillMonths){
14438                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
14439                 }
14440                     
14441                 fillMonths = {
14442                     tag: 'tr',
14443                     cn: []
14444                 };
14445                 
14446                 if(this.calendarWeeks){
14447                     // ISO 8601: First week contains first thursday.
14448                     // ISO also states week starts on Monday, but we can be more abstract here.
14449                     var
14450                     // Start of current week: based on weekstart/current date
14451                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
14452                     // Thursday of this week
14453                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
14454                     // First Thursday of year, year from thursday
14455                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
14456                     // Calendar week: ms between thursdays, div ms per day, div 7 days
14457                     calWeek =  (th - yth) / 864e5 / 7 + 1;
14458                     
14459                     fillMonths.cn.push({
14460                         tag: 'td',
14461                         cls: 'cw',
14462                         html: calWeek
14463                     });
14464                 }
14465             }
14466             
14467             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
14468                 clsName += ' old';
14469             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
14470                 clsName += ' new';
14471             }
14472             if (this.todayHighlight &&
14473                 prevMonth.getUTCFullYear() == today.getFullYear() &&
14474                 prevMonth.getUTCMonth() == today.getMonth() &&
14475                 prevMonth.getUTCDate() == today.getDate()) {
14476                 clsName += ' today';
14477             }
14478             
14479             if (currentDate && prevMonth.valueOf() === currentDate) {
14480                 clsName += ' active';
14481             }
14482             
14483             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
14484                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
14485                     clsName += ' disabled';
14486             }
14487             
14488             fillMonths.cn.push({
14489                 tag: 'td',
14490                 cls: 'day ' + clsName,
14491                 html: prevMonth.getDate()
14492             })
14493             
14494             prevMonth.setDate(prevMonth.getDate()+1);
14495         }
14496           
14497         var currentYear = this.date && this.date.getUTCFullYear();
14498         var currentMonth = this.date && this.date.getUTCMonth();
14499         
14500         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
14501         
14502         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
14503             v.removeClass('active');
14504             
14505             if(currentYear === year && k === currentMonth){
14506                 v.addClass('active');
14507             }
14508             
14509             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
14510                 v.addClass('disabled');
14511             }
14512             
14513         });
14514         
14515         
14516         year = parseInt(year/10, 10) * 10;
14517         
14518         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
14519         
14520         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
14521         
14522         year -= 1;
14523         for (var i = -1; i < 11; i++) {
14524             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
14525                 tag: 'span',
14526                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
14527                 html: year
14528             })
14529             
14530             year += 1;
14531         }
14532     },
14533     
14534     showMode: function(dir) 
14535     {
14536         if (dir) {
14537             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
14538         }
14539         Roo.each(this.picker().select('>div',true).elements, function(v){
14540             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14541             v.hide();
14542         });
14543         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
14544     },
14545     
14546     place: function()
14547     {
14548         if(this.isInline) return;
14549         
14550         this.picker().removeClass(['bottom', 'top']);
14551         
14552         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
14553             /*
14554              * place to the top of element!
14555              *
14556              */
14557             
14558             this.picker().addClass('top');
14559             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
14560             
14561             return;
14562         }
14563         
14564         this.picker().addClass('bottom');
14565         
14566         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
14567     },
14568     
14569     parseDate : function(value)
14570     {
14571         if(!value || value instanceof Date){
14572             return value;
14573         }
14574         var v = Date.parseDate(value, this.format);
14575         if (!v && this.useIso) {
14576             v = Date.parseDate(value, 'Y-m-d');
14577         }
14578         if(!v && this.altFormats){
14579             if(!this.altFormatsArray){
14580                 this.altFormatsArray = this.altFormats.split("|");
14581             }
14582             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
14583                 v = Date.parseDate(value, this.altFormatsArray[i]);
14584             }
14585         }
14586         return v;
14587     },
14588     
14589     formatDate : function(date, fmt)
14590     {
14591         return (!date || !(date instanceof Date)) ?
14592         date : date.dateFormat(fmt || this.format);
14593     },
14594     
14595     onFocus : function()
14596     {
14597         Roo.bootstrap.DateField.superclass.onFocus.call(this);
14598         this.show();
14599     },
14600     
14601     onBlur : function()
14602     {
14603         Roo.bootstrap.DateField.superclass.onBlur.call(this);
14604         
14605         var d = this.inputEl().getValue();
14606         
14607         if(d && d.length){
14608             this.setValue(d);
14609         }
14610                 
14611         this.hide();
14612     },
14613     
14614     show : function()
14615     {
14616         this.picker().show();
14617         this.update();
14618         this.place();
14619         
14620         this.fireEvent('show', this, this.date);
14621     },
14622     
14623     hide : function()
14624     {
14625         if(this.isInline) return;
14626         this.picker().hide();
14627         this.viewMode = this.startViewMode;
14628         this.showMode();
14629         
14630         this.fireEvent('hide', this, this.date);
14631         
14632     },
14633     
14634     onMousedown: function(e)
14635     {
14636         e.stopPropagation();
14637         e.preventDefault();
14638     },
14639     
14640     keyup: function(e)
14641     {
14642         Roo.bootstrap.DateField.superclass.keyup.call(this);
14643         this.update();
14644     },
14645
14646     setValue: function(v)
14647     {
14648         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
14649         
14650         var d = new Date(v);
14651         
14652         if(isNaN(d.getTime())){
14653             return;
14654         }
14655         
14656         this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
14657
14658         this.update();
14659
14660         this.fireEvent('select', this, this.date);
14661         
14662     },
14663     
14664     getValue: function()
14665     {
14666         return this.formatDate(this.date);
14667     },
14668     
14669     fireKey: function(e)
14670     {
14671         if (!this.picker().isVisible()){
14672             if (e.keyCode == 27) // allow escape to hide and re-show picker
14673                 this.show();
14674             return;
14675         }
14676         
14677         var dateChanged = false,
14678         dir, day, month,
14679         newDate, newViewDate;
14680         
14681         switch(e.keyCode){
14682             case 27: // escape
14683                 this.hide();
14684                 e.preventDefault();
14685                 break;
14686             case 37: // left
14687             case 39: // right
14688                 if (!this.keyboardNavigation) break;
14689                 dir = e.keyCode == 37 ? -1 : 1;
14690                 
14691                 if (e.ctrlKey){
14692                     newDate = this.moveYear(this.date, dir);
14693                     newViewDate = this.moveYear(this.viewDate, dir);
14694                 } else if (e.shiftKey){
14695                     newDate = this.moveMonth(this.date, dir);
14696                     newViewDate = this.moveMonth(this.viewDate, dir);
14697                 } else {
14698                     newDate = new Date(this.date);
14699                     newDate.setUTCDate(this.date.getUTCDate() + dir);
14700                     newViewDate = new Date(this.viewDate);
14701                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
14702                 }
14703                 if (this.dateWithinRange(newDate)){
14704                     this.date = newDate;
14705                     this.viewDate = newViewDate;
14706                     this.setValue(this.formatDate(this.date));
14707 //                    this.update();
14708                     e.preventDefault();
14709                     dateChanged = true;
14710                 }
14711                 break;
14712             case 38: // up
14713             case 40: // down
14714                 if (!this.keyboardNavigation) break;
14715                 dir = e.keyCode == 38 ? -1 : 1;
14716                 if (e.ctrlKey){
14717                     newDate = this.moveYear(this.date, dir);
14718                     newViewDate = this.moveYear(this.viewDate, dir);
14719                 } else if (e.shiftKey){
14720                     newDate = this.moveMonth(this.date, dir);
14721                     newViewDate = this.moveMonth(this.viewDate, dir);
14722                 } else {
14723                     newDate = new Date(this.date);
14724                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
14725                     newViewDate = new Date(this.viewDate);
14726                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
14727                 }
14728                 if (this.dateWithinRange(newDate)){
14729                     this.date = newDate;
14730                     this.viewDate = newViewDate;
14731                     this.setValue(this.formatDate(this.date));
14732 //                    this.update();
14733                     e.preventDefault();
14734                     dateChanged = true;
14735                 }
14736                 break;
14737             case 13: // enter
14738                 this.setValue(this.formatDate(this.date));
14739                 this.hide();
14740                 e.preventDefault();
14741                 break;
14742             case 9: // tab
14743                 this.setValue(this.formatDate(this.date));
14744                 this.hide();
14745                 break;
14746             case 16: // shift
14747             case 17: // ctrl
14748             case 18: // alt
14749                 break;
14750             default :
14751                 this.hide();
14752                 
14753         }
14754     },
14755     
14756     
14757     onClick: function(e) 
14758     {
14759         e.stopPropagation();
14760         e.preventDefault();
14761         
14762         var target = e.getTarget();
14763         
14764         if(target.nodeName.toLowerCase() === 'i'){
14765             target = Roo.get(target).dom.parentNode;
14766         }
14767         
14768         var nodeName = target.nodeName;
14769         var className = target.className;
14770         var html = target.innerHTML;
14771         
14772         switch(nodeName.toLowerCase()) {
14773             case 'th':
14774                 switch(className) {
14775                     case 'switch':
14776                         this.showMode(1);
14777                         break;
14778                     case 'prev':
14779                     case 'next':
14780                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
14781                         switch(this.viewMode){
14782                                 case 0:
14783                                         this.viewDate = this.moveMonth(this.viewDate, dir);
14784                                         break;
14785                                 case 1:
14786                                 case 2:
14787                                         this.viewDate = this.moveYear(this.viewDate, dir);
14788                                         break;
14789                         }
14790                         this.fill();
14791                         break;
14792                     case 'today':
14793                         var date = new Date();
14794                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
14795 //                        this.fill()
14796                         this.setValue(this.formatDate(this.date));
14797                         
14798                         this.hide();
14799                         break;
14800                 }
14801                 break;
14802             case 'span':
14803                 if (className.indexOf('disabled') === -1) {
14804                     this.viewDate.setUTCDate(1);
14805                     if (className.indexOf('month') !== -1) {
14806                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
14807                     } else {
14808                         var year = parseInt(html, 10) || 0;
14809                         this.viewDate.setUTCFullYear(year);
14810                         
14811                     }
14812                     this.showMode(-1);
14813                     this.fill();
14814                 }
14815                 break;
14816                 
14817             case 'td':
14818                 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
14819                     var day = parseInt(html, 10) || 1;
14820                     var year = this.viewDate.getUTCFullYear(),
14821                         month = this.viewDate.getUTCMonth();
14822
14823                     if (className.indexOf('old') !== -1) {
14824                         if(month === 0 ){
14825                             month = 11;
14826                             year -= 1;
14827                         }else{
14828                             month -= 1;
14829                         }
14830                     } else if (className.indexOf('new') !== -1) {
14831                         if (month == 11) {
14832                             month = 0;
14833                             year += 1;
14834                         } else {
14835                             month += 1;
14836                         }
14837                     }
14838                     this.date = this.UTCDate(year, month, day,0,0,0,0);
14839                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
14840 //                    this.fill();
14841                     this.setValue(this.formatDate(this.date));
14842                     this.hide();
14843                 }
14844                 break;
14845         }
14846     },
14847     
14848     setStartDate: function(startDate)
14849     {
14850         this.startDate = startDate || -Infinity;
14851         if (this.startDate !== -Infinity) {
14852             this.startDate = this.parseDate(this.startDate);
14853         }
14854         this.update();
14855         this.updateNavArrows();
14856     },
14857
14858     setEndDate: function(endDate)
14859     {
14860         this.endDate = endDate || Infinity;
14861         if (this.endDate !== Infinity) {
14862             this.endDate = this.parseDate(this.endDate);
14863         }
14864         this.update();
14865         this.updateNavArrows();
14866     },
14867     
14868     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
14869     {
14870         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
14871         if (typeof(this.daysOfWeekDisabled) !== 'object') {
14872             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
14873         }
14874         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
14875             return parseInt(d, 10);
14876         });
14877         this.update();
14878         this.updateNavArrows();
14879     },
14880     
14881     updateNavArrows: function() 
14882     {
14883         var d = new Date(this.viewDate),
14884         year = d.getUTCFullYear(),
14885         month = d.getUTCMonth();
14886         
14887         Roo.each(this.picker().select('.prev', true).elements, function(v){
14888             v.show();
14889             switch (this.viewMode) {
14890                 case 0:
14891
14892                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
14893                         v.hide();
14894                     }
14895                     break;
14896                 case 1:
14897                 case 2:
14898                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
14899                         v.hide();
14900                     }
14901                     break;
14902             }
14903         });
14904         
14905         Roo.each(this.picker().select('.next', true).elements, function(v){
14906             v.show();
14907             switch (this.viewMode) {
14908                 case 0:
14909
14910                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
14911                         v.hide();
14912                     }
14913                     break;
14914                 case 1:
14915                 case 2:
14916                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
14917                         v.hide();
14918                     }
14919                     break;
14920             }
14921         })
14922     },
14923     
14924     moveMonth: function(date, dir)
14925     {
14926         if (!dir) return date;
14927         var new_date = new Date(date.valueOf()),
14928         day = new_date.getUTCDate(),
14929         month = new_date.getUTCMonth(),
14930         mag = Math.abs(dir),
14931         new_month, test;
14932         dir = dir > 0 ? 1 : -1;
14933         if (mag == 1){
14934             test = dir == -1
14935             // If going back one month, make sure month is not current month
14936             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
14937             ? function(){
14938                 return new_date.getUTCMonth() == month;
14939             }
14940             // If going forward one month, make sure month is as expected
14941             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
14942             : function(){
14943                 return new_date.getUTCMonth() != new_month;
14944             };
14945             new_month = month + dir;
14946             new_date.setUTCMonth(new_month);
14947             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
14948             if (new_month < 0 || new_month > 11)
14949                 new_month = (new_month + 12) % 12;
14950         } else {
14951             // For magnitudes >1, move one month at a time...
14952             for (var i=0; i<mag; i++)
14953                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
14954                 new_date = this.moveMonth(new_date, dir);
14955             // ...then reset the day, keeping it in the new month
14956             new_month = new_date.getUTCMonth();
14957             new_date.setUTCDate(day);
14958             test = function(){
14959                 return new_month != new_date.getUTCMonth();
14960             };
14961         }
14962         // Common date-resetting loop -- if date is beyond end of month, make it
14963         // end of month
14964         while (test()){
14965             new_date.setUTCDate(--day);
14966             new_date.setUTCMonth(new_month);
14967         }
14968         return new_date;
14969     },
14970
14971     moveYear: function(date, dir)
14972     {
14973         return this.moveMonth(date, dir*12);
14974     },
14975
14976     dateWithinRange: function(date)
14977     {
14978         return date >= this.startDate && date <= this.endDate;
14979     },
14980
14981     
14982     remove: function() 
14983     {
14984         this.picker().remove();
14985     }
14986    
14987 });
14988
14989 Roo.apply(Roo.bootstrap.DateField,  {
14990     
14991     head : {
14992         tag: 'thead',
14993         cn: [
14994         {
14995             tag: 'tr',
14996             cn: [
14997             {
14998                 tag: 'th',
14999                 cls: 'prev',
15000                 html: '<i class="fa fa-arrow-left"/>'
15001             },
15002             {
15003                 tag: 'th',
15004                 cls: 'switch',
15005                 colspan: '5'
15006             },
15007             {
15008                 tag: 'th',
15009                 cls: 'next',
15010                 html: '<i class="fa fa-arrow-right"/>'
15011             }
15012
15013             ]
15014         }
15015         ]
15016     },
15017     
15018     content : {
15019         tag: 'tbody',
15020         cn: [
15021         {
15022             tag: 'tr',
15023             cn: [
15024             {
15025                 tag: 'td',
15026                 colspan: '7'
15027             }
15028             ]
15029         }
15030         ]
15031     },
15032     
15033     footer : {
15034         tag: 'tfoot',
15035         cn: [
15036         {
15037             tag: 'tr',
15038             cn: [
15039             {
15040                 tag: 'th',
15041                 colspan: '7',
15042                 cls: 'today'
15043             }
15044                     
15045             ]
15046         }
15047         ]
15048     },
15049     
15050     dates:{
15051         en: {
15052             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
15053             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
15054             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
15055             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
15056             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
15057             today: "Today"
15058         }
15059     },
15060     
15061     modes: [
15062     {
15063         clsName: 'days',
15064         navFnc: 'Month',
15065         navStep: 1
15066     },
15067     {
15068         clsName: 'months',
15069         navFnc: 'FullYear',
15070         navStep: 1
15071     },
15072     {
15073         clsName: 'years',
15074         navFnc: 'FullYear',
15075         navStep: 10
15076     }]
15077 });
15078
15079 Roo.apply(Roo.bootstrap.DateField,  {
15080   
15081     template : {
15082         tag: 'div',
15083         cls: 'datepicker dropdown-menu',
15084         cn: [
15085         {
15086             tag: 'div',
15087             cls: 'datepicker-days',
15088             cn: [
15089             {
15090                 tag: 'table',
15091                 cls: 'table-condensed',
15092                 cn:[
15093                 Roo.bootstrap.DateField.head,
15094                 {
15095                     tag: 'tbody'
15096                 },
15097                 Roo.bootstrap.DateField.footer
15098                 ]
15099             }
15100             ]
15101         },
15102         {
15103             tag: 'div',
15104             cls: 'datepicker-months',
15105             cn: [
15106             {
15107                 tag: 'table',
15108                 cls: 'table-condensed',
15109                 cn:[
15110                 Roo.bootstrap.DateField.head,
15111                 Roo.bootstrap.DateField.content,
15112                 Roo.bootstrap.DateField.footer
15113                 ]
15114             }
15115             ]
15116         },
15117         {
15118             tag: 'div',
15119             cls: 'datepicker-years',
15120             cn: [
15121             {
15122                 tag: 'table',
15123                 cls: 'table-condensed',
15124                 cn:[
15125                 Roo.bootstrap.DateField.head,
15126                 Roo.bootstrap.DateField.content,
15127                 Roo.bootstrap.DateField.footer
15128                 ]
15129             }
15130             ]
15131         }
15132         ]
15133     }
15134 });
15135
15136  
15137
15138  /*
15139  * - LGPL
15140  *
15141  * TimeField
15142  * 
15143  */
15144
15145 /**
15146  * @class Roo.bootstrap.TimeField
15147  * @extends Roo.bootstrap.Input
15148  * Bootstrap DateField class
15149  * 
15150  * 
15151  * @constructor
15152  * Create a new TimeField
15153  * @param {Object} config The config object
15154  */
15155
15156 Roo.bootstrap.TimeField = function(config){
15157     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
15158     this.addEvents({
15159             /**
15160              * @event show
15161              * Fires when this field show.
15162              * @param {Roo.bootstrap.DateField} this
15163              * @param {Mixed} date The date value
15164              */
15165             show : true,
15166             /**
15167              * @event show
15168              * Fires when this field hide.
15169              * @param {Roo.bootstrap.DateField} this
15170              * @param {Mixed} date The date value
15171              */
15172             hide : true,
15173             /**
15174              * @event select
15175              * Fires when select a date.
15176              * @param {Roo.bootstrap.DateField} this
15177              * @param {Mixed} date The date value
15178              */
15179             select : true
15180         });
15181 };
15182
15183 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
15184     
15185     /**
15186      * @cfg {String} format
15187      * The default time format string which can be overriden for localization support.  The format must be
15188      * valid according to {@link Date#parseDate} (defaults to 'H:i').
15189      */
15190     format : "H:i",
15191        
15192     onRender: function(ct, position)
15193     {
15194         
15195         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
15196                 
15197         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
15198         
15199         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15200         
15201         this.pop = this.picker().select('>.datepicker-time',true).first();
15202         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block' 
15203         
15204         this.picker().on('mousedown', this.onMousedown, this);
15205         this.picker().on('click', this.onClick, this);
15206         
15207         this.picker().addClass('datepicker-dropdown');
15208     
15209         this.fillTime();
15210         this.update();
15211             
15212         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
15213         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
15214         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
15215         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
15216         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
15217         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
15218
15219     },
15220     
15221     fireKey: function(e){
15222         if (!this.picker().isVisible()){
15223             if (e.keyCode == 27) // allow escape to hide and re-show picker
15224                 this.show();
15225             return;
15226         }
15227
15228         e.preventDefault();
15229         
15230         switch(e.keyCode){
15231             case 27: // escape
15232                 this.hide();
15233                 break;
15234             case 37: // left
15235             case 39: // right
15236                 this.onTogglePeriod();
15237                 break;
15238             case 38: // up
15239                 this.onIncrementMinutes();
15240                 break;
15241             case 40: // down
15242                 this.onDecrementMinutes();
15243                 break;
15244             case 13: // enter
15245             case 9: // tab
15246                 this.setTime();
15247                 break;
15248         }
15249     },
15250     
15251     onClick: function(e) {
15252         e.stopPropagation();
15253         e.preventDefault();
15254     },
15255     
15256     picker : function()
15257     {
15258         return this.el.select('.datepicker', true).first();
15259     },
15260     
15261     fillTime: function()
15262     {    
15263         var time = this.pop.select('tbody', true).first();
15264         
15265         time.dom.innerHTML = '';
15266         
15267         time.createChild({
15268             tag: 'tr',
15269             cn: [
15270                 {
15271                     tag: 'td',
15272                     cn: [
15273                         {
15274                             tag: 'a',
15275                             href: '#',
15276                             cls: 'btn',
15277                             cn: [
15278                                 {
15279                                     tag: 'span',
15280                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
15281                                 }
15282                             ]
15283                         } 
15284                     ]
15285                 },
15286                 {
15287                     tag: 'td',
15288                     cls: 'separator'
15289                 },
15290                 {
15291                     tag: 'td',
15292                     cn: [
15293                         {
15294                             tag: 'a',
15295                             href: '#',
15296                             cls: 'btn',
15297                             cn: [
15298                                 {
15299                                     tag: 'span',
15300                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
15301                                 }
15302                             ]
15303                         }
15304                     ]
15305                 },
15306                 {
15307                     tag: 'td',
15308                     cls: 'separator'
15309                 }
15310             ]
15311         });
15312         
15313         time.createChild({
15314             tag: 'tr',
15315             cn: [
15316                 {
15317                     tag: 'td',
15318                     cn: [
15319                         {
15320                             tag: 'span',
15321                             cls: 'timepicker-hour',
15322                             html: '00'
15323                         }  
15324                     ]
15325                 },
15326                 {
15327                     tag: 'td',
15328                     cls: 'separator',
15329                     html: ':'
15330                 },
15331                 {
15332                     tag: 'td',
15333                     cn: [
15334                         {
15335                             tag: 'span',
15336                             cls: 'timepicker-minute',
15337                             html: '00'
15338                         }  
15339                     ]
15340                 },
15341                 {
15342                     tag: 'td',
15343                     cls: 'separator'
15344                 },
15345                 {
15346                     tag: 'td',
15347                     cn: [
15348                         {
15349                             tag: 'button',
15350                             type: 'button',
15351                             cls: 'btn btn-primary period',
15352                             html: 'AM'
15353                             
15354                         }
15355                     ]
15356                 }
15357             ]
15358         });
15359         
15360         time.createChild({
15361             tag: 'tr',
15362             cn: [
15363                 {
15364                     tag: 'td',
15365                     cn: [
15366                         {
15367                             tag: 'a',
15368                             href: '#',
15369                             cls: 'btn',
15370                             cn: [
15371                                 {
15372                                     tag: 'span',
15373                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
15374                                 }
15375                             ]
15376                         }
15377                     ]
15378                 },
15379                 {
15380                     tag: 'td',
15381                     cls: 'separator'
15382                 },
15383                 {
15384                     tag: 'td',
15385                     cn: [
15386                         {
15387                             tag: 'a',
15388                             href: '#',
15389                             cls: 'btn',
15390                             cn: [
15391                                 {
15392                                     tag: 'span',
15393                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
15394                                 }
15395                             ]
15396                         }
15397                     ]
15398                 },
15399                 {
15400                     tag: 'td',
15401                     cls: 'separator'
15402                 }
15403             ]
15404         });
15405         
15406     },
15407     
15408     update: function()
15409     {
15410         
15411         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
15412         
15413         this.fill();
15414     },
15415     
15416     fill: function() 
15417     {
15418         var hours = this.time.getHours();
15419         var minutes = this.time.getMinutes();
15420         var period = 'AM';
15421         
15422         if(hours > 11){
15423             period = 'PM';
15424         }
15425         
15426         if(hours == 0){
15427             hours = 12;
15428         }
15429         
15430         
15431         if(hours > 12){
15432             hours = hours - 12;
15433         }
15434         
15435         if(hours < 10){
15436             hours = '0' + hours;
15437         }
15438         
15439         if(minutes < 10){
15440             minutes = '0' + minutes;
15441         }
15442         
15443         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
15444         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
15445         this.pop.select('button', true).first().dom.innerHTML = period;
15446         
15447     },
15448     
15449     place: function()
15450     {   
15451         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
15452         
15453         var cls = ['bottom'];
15454         
15455         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
15456             cls.pop();
15457             cls.push('top');
15458         }
15459         
15460         cls.push('right');
15461         
15462         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
15463             cls.pop();
15464             cls.push('left');
15465         }
15466         
15467         this.picker().addClass(cls.join('-'));
15468         
15469         var _this = this;
15470         
15471         Roo.each(cls, function(c){
15472             if(c == 'bottom'){
15473                 _this.picker().setTop(_this.inputEl().getHeight());
15474                 return;
15475             }
15476             if(c == 'top'){
15477                 _this.picker().setTop(0 - _this.picker().getHeight());
15478                 return;
15479             }
15480             
15481             if(c == 'left'){
15482                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
15483                 return;
15484             }
15485             if(c == 'right'){
15486                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
15487                 return;
15488             }
15489         });
15490         
15491     },
15492   
15493     onFocus : function()
15494     {
15495         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
15496         this.show();
15497     },
15498     
15499     onBlur : function()
15500     {
15501         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
15502         this.hide();
15503     },
15504     
15505     show : function()
15506     {
15507         this.picker().show();
15508         this.pop.show();
15509         this.update();
15510         this.place();
15511         
15512         this.fireEvent('show', this, this.date);
15513     },
15514     
15515     hide : function()
15516     {
15517         this.picker().hide();
15518         this.pop.hide();
15519         
15520         this.fireEvent('hide', this, this.date);
15521     },
15522     
15523     setTime : function()
15524     {
15525         this.hide();
15526         this.setValue(this.time.format(this.format));
15527         
15528         this.fireEvent('select', this, this.date);
15529         
15530         
15531     },
15532     
15533     onMousedown: function(e){
15534         e.stopPropagation();
15535         e.preventDefault();
15536     },
15537     
15538     onIncrementHours: function()
15539     {
15540         Roo.log('onIncrementHours');
15541         this.time = this.time.add(Date.HOUR, 1);
15542         this.update();
15543         
15544     },
15545     
15546     onDecrementHours: function()
15547     {
15548         Roo.log('onDecrementHours');
15549         this.time = this.time.add(Date.HOUR, -1);
15550         this.update();
15551     },
15552     
15553     onIncrementMinutes: function()
15554     {
15555         Roo.log('onIncrementMinutes');
15556         this.time = this.time.add(Date.MINUTE, 1);
15557         this.update();
15558     },
15559     
15560     onDecrementMinutes: function()
15561     {
15562         Roo.log('onDecrementMinutes');
15563         this.time = this.time.add(Date.MINUTE, -1);
15564         this.update();
15565     },
15566     
15567     onTogglePeriod: function()
15568     {
15569         Roo.log('onTogglePeriod');
15570         this.time = this.time.add(Date.HOUR, 12);
15571         this.update();
15572     }
15573     
15574    
15575 });
15576
15577 Roo.apply(Roo.bootstrap.TimeField,  {
15578     
15579     content : {
15580         tag: 'tbody',
15581         cn: [
15582             {
15583                 tag: 'tr',
15584                 cn: [
15585                 {
15586                     tag: 'td',
15587                     colspan: '7'
15588                 }
15589                 ]
15590             }
15591         ]
15592     },
15593     
15594     footer : {
15595         tag: 'tfoot',
15596         cn: [
15597             {
15598                 tag: 'tr',
15599                 cn: [
15600                 {
15601                     tag: 'th',
15602                     colspan: '7',
15603                     cls: '',
15604                     cn: [
15605                         {
15606                             tag: 'button',
15607                             cls: 'btn btn-info ok',
15608                             html: 'OK'
15609                         }
15610                     ]
15611                 }
15612
15613                 ]
15614             }
15615         ]
15616     }
15617 });
15618
15619 Roo.apply(Roo.bootstrap.TimeField,  {
15620   
15621     template : {
15622         tag: 'div',
15623         cls: 'datepicker dropdown-menu',
15624         cn: [
15625             {
15626                 tag: 'div',
15627                 cls: 'datepicker-time',
15628                 cn: [
15629                 {
15630                     tag: 'table',
15631                     cls: 'table-condensed',
15632                     cn:[
15633                     Roo.bootstrap.TimeField.content,
15634                     Roo.bootstrap.TimeField.footer
15635                     ]
15636                 }
15637                 ]
15638             }
15639         ]
15640     }
15641 });
15642
15643  
15644
15645  /*
15646  * - LGPL
15647  *
15648  * CheckBox
15649  * 
15650  */
15651
15652 /**
15653  * @class Roo.bootstrap.CheckBox
15654  * @extends Roo.bootstrap.Input
15655  * Bootstrap CheckBox class
15656  * 
15657  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
15658  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
15659  * @cfg {String} boxLabel The text that appears beside the checkbox
15660  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
15661  * @cfg {Boolean} checked initnal the element
15662  * 
15663  * 
15664  * @constructor
15665  * Create a new CheckBox
15666  * @param {Object} config The config object
15667  */
15668
15669 Roo.bootstrap.CheckBox = function(config){
15670     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
15671    
15672         this.addEvents({
15673             /**
15674             * @event check
15675             * Fires when the element is checked or unchecked.
15676             * @param {Roo.bootstrap.CheckBox} this This input
15677             * @param {Boolean} checked The new checked value
15678             */
15679            check : true
15680         });
15681 };
15682
15683 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
15684     
15685     inputType: 'checkbox',
15686     inputValue: 1,
15687     valueOff: 0,
15688     boxLabel: false,
15689     checked: false,
15690     weight : false,
15691     
15692     getAutoCreate : function()
15693     {
15694         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
15695         
15696         var id = Roo.id();
15697         
15698         var cfg = {};
15699         
15700         cfg.cls = 'form-group checkbox' //input-group
15701         
15702         
15703         
15704         
15705         var input =  {
15706             tag: 'input',
15707             id : id,
15708             type : this.inputType,
15709             value : (!this.checked) ? this.valueOff : this.inputValue,
15710             cls : 'roo-checkbox', //'form-box',
15711             placeholder : this.placeholder || ''
15712             
15713         };
15714         
15715         if (this.weight) { // Validity check?
15716             cfg.cls += " checkbox-" + this.weight;
15717         }
15718         
15719         if (this.disabled) {
15720             input.disabled=true;
15721         }
15722         
15723         if(this.checked){
15724             input.checked = this.checked;
15725         }
15726         
15727         if (this.name) {
15728             input.name = this.name;
15729         }
15730         
15731         if (this.size) {
15732             input.cls += ' input-' + this.size;
15733         }
15734         
15735         var settings=this;
15736         ['xs','sm','md','lg'].map(function(size){
15737             if (settings[size]) {
15738                 cfg.cls += ' col-' + size + '-' + settings[size];
15739             }
15740         });
15741         
15742        
15743         
15744         var inputblock = input;
15745         
15746         
15747         
15748         
15749         if (this.before || this.after) {
15750             
15751             inputblock = {
15752                 cls : 'input-group',
15753                 cn :  [] 
15754             };
15755             if (this.before) {
15756                 inputblock.cn.push({
15757                     tag :'span',
15758                     cls : 'input-group-addon',
15759                     html : this.before
15760                 });
15761             }
15762             inputblock.cn.push(input);
15763             if (this.after) {
15764                 inputblock.cn.push({
15765                     tag :'span',
15766                     cls : 'input-group-addon',
15767                     html : this.after
15768                 });
15769             }
15770             
15771         };
15772         
15773         if (align ==='left' && this.fieldLabel.length) {
15774                 Roo.log("left and has label");
15775                 cfg.cn = [
15776                     
15777                     {
15778                         tag: 'label',
15779                         'for' :  id,
15780                         cls : 'control-label col-md-' + this.labelWidth,
15781                         html : this.fieldLabel
15782                         
15783                     },
15784                     {
15785                         cls : "col-md-" + (12 - this.labelWidth), 
15786                         cn: [
15787                             inputblock
15788                         ]
15789                     }
15790                     
15791                 ];
15792         } else if ( this.fieldLabel.length) {
15793                 Roo.log(" label");
15794                 cfg.cn = [
15795                    
15796                     {
15797                         tag: this.boxLabel ? 'span' : 'label',
15798                         'for': id,
15799                         cls: 'control-label box-input-label',
15800                         //cls : 'input-group-addon',
15801                         html : this.fieldLabel
15802                         
15803                     },
15804                     
15805                     inputblock
15806                     
15807                 ];
15808
15809         } else {
15810             
15811                 Roo.log(" no label && no align");
15812                 cfg.cn = [  inputblock ] ;
15813                 
15814                 
15815         };
15816          if(this.boxLabel){
15817             cfg.cn.push( {
15818                 tag: 'label',
15819                 'for': id,
15820                 cls: 'box-label',
15821                 html: this.boxLabel
15822                 
15823             });
15824         }
15825         
15826         
15827        
15828         return cfg;
15829         
15830     },
15831     
15832     /**
15833      * return the real input element.
15834      */
15835     inputEl: function ()
15836     {
15837         return this.el.select('input.roo-checkbox',true).first();
15838     },
15839     
15840     label: function()
15841     {
15842         return this.el.select('label.control-label',true).first();
15843     },
15844     
15845     initEvents : function()
15846     {
15847 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
15848         
15849         this.inputEl().on('click', this.onClick,  this);
15850         
15851     },
15852     
15853     onClick : function()
15854     {   
15855         this.setChecked(!this.checked);
15856     },
15857     
15858     setChecked : function(state,suppressEvent)
15859     {
15860         this.checked = state;
15861         
15862         this.inputEl().dom.checked = state;
15863         
15864         if(suppressEvent !== true){
15865             this.fireEvent('check', this, state);
15866         }
15867         
15868         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
15869         
15870     },
15871     
15872     setValue : function(v,suppressEvent)
15873     {
15874         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
15875     }
15876     
15877 });
15878
15879  
15880 /*
15881  * - LGPL
15882  *
15883  * Radio
15884  * 
15885  */
15886
15887 /**
15888  * @class Roo.bootstrap.Radio
15889  * @extends Roo.bootstrap.CheckBox
15890  * Bootstrap Radio class
15891
15892  * @constructor
15893  * Create a new Radio
15894  * @param {Object} config The config object
15895  */
15896
15897 Roo.bootstrap.Radio = function(config){
15898     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
15899    
15900 };
15901
15902 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
15903     
15904     inputType: 'radio',
15905     inputValue: '',
15906     valueOff: '',
15907     
15908     getAutoCreate : function()
15909     {
15910         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
15911         
15912         var id = Roo.id();
15913         
15914         var cfg = {};
15915         
15916         cfg.cls = 'form-group radio' //input-group
15917         
15918         var input =  {
15919             tag: 'input',
15920             id : id,
15921             type : this.inputType,
15922             value : (!this.checked) ? this.valueOff : this.inputValue,
15923             cls : 'roo-radio',
15924             placeholder : this.placeholder || ''
15925             
15926         };
15927           if (this.weight) { // Validity check?
15928             cfg.cls += " radio-" + this.weight;
15929         }
15930         if (this.disabled) {
15931             input.disabled=true;
15932         }
15933         
15934         if(this.checked){
15935             input.checked = this.checked;
15936         }
15937         
15938         if (this.name) {
15939             input.name = this.name;
15940         }
15941         
15942         if (this.size) {
15943             input.cls += ' input-' + this.size;
15944         }
15945         
15946         var settings=this;
15947         ['xs','sm','md','lg'].map(function(size){
15948             if (settings[size]) {
15949                 cfg.cls += ' col-' + size + '-' + settings[size];
15950             }
15951         });
15952         
15953         var inputblock = input;
15954         
15955         if (this.before || this.after) {
15956             
15957             inputblock = {
15958                 cls : 'input-group',
15959                 cn :  [] 
15960             };
15961             if (this.before) {
15962                 inputblock.cn.push({
15963                     tag :'span',
15964                     cls : 'input-group-addon',
15965                     html : this.before
15966                 });
15967             }
15968             inputblock.cn.push(input);
15969             if (this.after) {
15970                 inputblock.cn.push({
15971                     tag :'span',
15972                     cls : 'input-group-addon',
15973                     html : this.after
15974                 });
15975             }
15976             
15977         };
15978         
15979         if (align ==='left' && this.fieldLabel.length) {
15980                 Roo.log("left and has label");
15981                 cfg.cn = [
15982                     
15983                     {
15984                         tag: 'label',
15985                         'for' :  id,
15986                         cls : 'control-label col-md-' + this.labelWidth,
15987                         html : this.fieldLabel
15988                         
15989                     },
15990                     {
15991                         cls : "col-md-" + (12 - this.labelWidth), 
15992                         cn: [
15993                             inputblock
15994                         ]
15995                     }
15996                     
15997                 ];
15998         } else if ( this.fieldLabel.length) {
15999                 Roo.log(" label");
16000                  cfg.cn = [
16001                    
16002                     {
16003                         tag: 'label',
16004                         'for': id,
16005                         cls: 'control-label box-input-label',
16006                         //cls : 'input-group-addon',
16007                         html : this.fieldLabel
16008                         
16009                     },
16010                     
16011                     inputblock
16012                     
16013                 ];
16014
16015         } else {
16016             
16017                    Roo.log(" no label && no align");
16018                 cfg.cn = [
16019                     
16020                         inputblock
16021                     
16022                 ];
16023                 
16024                 
16025         };
16026         
16027         if(this.boxLabel){
16028             cfg.cn.push({
16029                 tag: 'label',
16030                 'for': id,
16031                 cls: 'box-label',
16032                 html: this.boxLabel
16033             })
16034         }
16035         
16036         return cfg;
16037         
16038     },
16039     inputEl: function ()
16040     {
16041         return this.el.select('input.roo-radio',true).first();
16042     },
16043     onClick : function()
16044     {   
16045         this.setChecked(true);
16046     },
16047     
16048     setChecked : function(state,suppressEvent)
16049     {
16050         if(state){
16051             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16052                 v.dom.checked = false;
16053             });
16054         }
16055         
16056         this.checked = state;
16057         this.inputEl().dom.checked = state;
16058         
16059         if(suppressEvent !== true){
16060             this.fireEvent('check', this, state);
16061         }
16062         
16063         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
16064         
16065     },
16066     
16067     getGroupValue : function()
16068     {
16069         var value = ''
16070         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16071             if(v.dom.checked == true){
16072                 value = v.dom.value;
16073             }
16074         });
16075         
16076         return value;
16077     },
16078     
16079     /**
16080      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
16081      * @return {Mixed} value The field value
16082      */
16083     getValue : function(){
16084         return this.getGroupValue();
16085     }
16086     
16087 });
16088
16089  
16090 //<script type="text/javascript">
16091
16092 /*
16093  * Based  Ext JS Library 1.1.1
16094  * Copyright(c) 2006-2007, Ext JS, LLC.
16095  * LGPL
16096  *
16097  */
16098  
16099 /**
16100  * @class Roo.HtmlEditorCore
16101  * @extends Roo.Component
16102  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
16103  *
16104  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
16105  */
16106
16107 Roo.HtmlEditorCore = function(config){
16108     
16109     
16110     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
16111     this.addEvents({
16112         /**
16113          * @event initialize
16114          * Fires when the editor is fully initialized (including the iframe)
16115          * @param {Roo.HtmlEditorCore} this
16116          */
16117         initialize: true,
16118         /**
16119          * @event activate
16120          * Fires when the editor is first receives the focus. Any insertion must wait
16121          * until after this event.
16122          * @param {Roo.HtmlEditorCore} this
16123          */
16124         activate: true,
16125          /**
16126          * @event beforesync
16127          * Fires before the textarea is updated with content from the editor iframe. Return false
16128          * to cancel the sync.
16129          * @param {Roo.HtmlEditorCore} this
16130          * @param {String} html
16131          */
16132         beforesync: true,
16133          /**
16134          * @event beforepush
16135          * Fires before the iframe editor is updated with content from the textarea. Return false
16136          * to cancel the push.
16137          * @param {Roo.HtmlEditorCore} this
16138          * @param {String} html
16139          */
16140         beforepush: true,
16141          /**
16142          * @event sync
16143          * Fires when the textarea is updated with content from the editor iframe.
16144          * @param {Roo.HtmlEditorCore} this
16145          * @param {String} html
16146          */
16147         sync: true,
16148          /**
16149          * @event push
16150          * Fires when the iframe editor is updated with content from the textarea.
16151          * @param {Roo.HtmlEditorCore} this
16152          * @param {String} html
16153          */
16154         push: true,
16155         
16156         /**
16157          * @event editorevent
16158          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
16159          * @param {Roo.HtmlEditorCore} this
16160          */
16161         editorevent: true
16162     });
16163      
16164 };
16165
16166
16167 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
16168
16169
16170      /**
16171      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
16172      */
16173     
16174     owner : false,
16175     
16176      /**
16177      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
16178      *                        Roo.resizable.
16179      */
16180     resizable : false,
16181      /**
16182      * @cfg {Number} height (in pixels)
16183      */   
16184     height: 300,
16185    /**
16186      * @cfg {Number} width (in pixels)
16187      */   
16188     width: 500,
16189     
16190     /**
16191      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
16192      * 
16193      */
16194     stylesheets: false,
16195     
16196     // id of frame..
16197     frameId: false,
16198     
16199     // private properties
16200     validationEvent : false,
16201     deferHeight: true,
16202     initialized : false,
16203     activated : false,
16204     sourceEditMode : false,
16205     onFocus : Roo.emptyFn,
16206     iframePad:3,
16207     hideMode:'offsets',
16208     
16209     clearUp: true,
16210     
16211      
16212     
16213
16214     /**
16215      * Protected method that will not generally be called directly. It
16216      * is called when the editor initializes the iframe with HTML contents. Override this method if you
16217      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
16218      */
16219     getDocMarkup : function(){
16220         // body styles..
16221         var st = '';
16222         Roo.log(this.stylesheets);
16223         
16224         // inherit styels from page...?? 
16225         if (this.stylesheets === false) {
16226             
16227             Roo.get(document.head).select('style').each(function(node) {
16228                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16229             });
16230             
16231             Roo.get(document.head).select('link').each(function(node) { 
16232                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16233             });
16234             
16235         } else if (!this.stylesheets.length) {
16236                 // simple..
16237                 st = '<style type="text/css">' +
16238                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16239                    '</style>';
16240         } else {
16241             Roo.each(this.stylesheets, function(s) {
16242                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
16243             });
16244             
16245         }
16246         
16247         st +=  '<style type="text/css">' +
16248             'IMG { cursor: pointer } ' +
16249         '</style>';
16250
16251         
16252         return '<html><head>' + st  +
16253             //<style type="text/css">' +
16254             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16255             //'</style>' +
16256             ' </head><body class="roo-htmleditor-body"></body></html>';
16257     },
16258
16259     // private
16260     onRender : function(ct, position)
16261     {
16262         var _t = this;
16263         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
16264         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
16265         
16266         
16267         this.el.dom.style.border = '0 none';
16268         this.el.dom.setAttribute('tabIndex', -1);
16269         this.el.addClass('x-hidden hide');
16270         
16271         
16272         
16273         if(Roo.isIE){ // fix IE 1px bogus margin
16274             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
16275         }
16276        
16277         
16278         this.frameId = Roo.id();
16279         
16280          
16281         
16282         var iframe = this.owner.wrap.createChild({
16283             tag: 'iframe',
16284             cls: 'form-control', // bootstrap..
16285             id: this.frameId,
16286             name: this.frameId,
16287             frameBorder : 'no',
16288             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
16289         }, this.el
16290         );
16291         
16292         
16293         this.iframe = iframe.dom;
16294
16295          this.assignDocWin();
16296         
16297         this.doc.designMode = 'on';
16298        
16299         this.doc.open();
16300         this.doc.write(this.getDocMarkup());
16301         this.doc.close();
16302
16303         
16304         var task = { // must defer to wait for browser to be ready
16305             run : function(){
16306                 //console.log("run task?" + this.doc.readyState);
16307                 this.assignDocWin();
16308                 if(this.doc.body || this.doc.readyState == 'complete'){
16309                     try {
16310                         this.doc.designMode="on";
16311                     } catch (e) {
16312                         return;
16313                     }
16314                     Roo.TaskMgr.stop(task);
16315                     this.initEditor.defer(10, this);
16316                 }
16317             },
16318             interval : 10,
16319             duration: 10000,
16320             scope: this
16321         };
16322         Roo.TaskMgr.start(task);
16323
16324         
16325          
16326     },
16327
16328     // private
16329     onResize : function(w, h)
16330     {
16331          Roo.log('resize: ' +w + ',' + h );
16332         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
16333         if(!this.iframe){
16334             return;
16335         }
16336         if(typeof w == 'number'){
16337             
16338             this.iframe.style.width = w + 'px';
16339         }
16340         if(typeof h == 'number'){
16341             
16342             this.iframe.style.height = h + 'px';
16343             if(this.doc){
16344                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
16345             }
16346         }
16347         
16348     },
16349
16350     /**
16351      * Toggles the editor between standard and source edit mode.
16352      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
16353      */
16354     toggleSourceEdit : function(sourceEditMode){
16355         
16356         this.sourceEditMode = sourceEditMode === true;
16357         
16358         if(this.sourceEditMode){
16359  
16360             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
16361             
16362         }else{
16363             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
16364             //this.iframe.className = '';
16365             this.deferFocus();
16366         }
16367         //this.setSize(this.owner.wrap.getSize());
16368         //this.fireEvent('editmodechange', this, this.sourceEditMode);
16369     },
16370
16371     
16372   
16373
16374     /**
16375      * Protected method that will not generally be called directly. If you need/want
16376      * custom HTML cleanup, this is the method you should override.
16377      * @param {String} html The HTML to be cleaned
16378      * return {String} The cleaned HTML
16379      */
16380     cleanHtml : function(html){
16381         html = String(html);
16382         if(html.length > 5){
16383             if(Roo.isSafari){ // strip safari nonsense
16384                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
16385             }
16386         }
16387         if(html == '&nbsp;'){
16388             html = '';
16389         }
16390         return html;
16391     },
16392
16393     /**
16394      * HTML Editor -> Textarea
16395      * Protected method that will not generally be called directly. Syncs the contents
16396      * of the editor iframe with the textarea.
16397      */
16398     syncValue : function(){
16399         if(this.initialized){
16400             var bd = (this.doc.body || this.doc.documentElement);
16401             //this.cleanUpPaste(); -- this is done else where and causes havoc..
16402             var html = bd.innerHTML;
16403             if(Roo.isSafari){
16404                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
16405                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
16406                 if(m && m[1]){
16407                     html = '<div style="'+m[0]+'">' + html + '</div>';
16408                 }
16409             }
16410             html = this.cleanHtml(html);
16411             // fix up the special chars.. normaly like back quotes in word...
16412             // however we do not want to do this with chinese..
16413             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
16414                 var cc = b.charCodeAt();
16415                 if (
16416                     (cc >= 0x4E00 && cc < 0xA000 ) ||
16417                     (cc >= 0x3400 && cc < 0x4E00 ) ||
16418                     (cc >= 0xf900 && cc < 0xfb00 )
16419                 ) {
16420                         return b;
16421                 }
16422                 return "&#"+cc+";" 
16423             });
16424             if(this.owner.fireEvent('beforesync', this, html) !== false){
16425                 this.el.dom.value = html;
16426                 this.owner.fireEvent('sync', this, html);
16427             }
16428         }
16429     },
16430
16431     /**
16432      * Protected method that will not generally be called directly. Pushes the value of the textarea
16433      * into the iframe editor.
16434      */
16435     pushValue : function(){
16436         if(this.initialized){
16437             var v = this.el.dom.value.trim();
16438             
16439 //            if(v.length < 1){
16440 //                v = '&#160;';
16441 //            }
16442             
16443             if(this.owner.fireEvent('beforepush', this, v) !== false){
16444                 var d = (this.doc.body || this.doc.documentElement);
16445                 d.innerHTML = v;
16446                 this.cleanUpPaste();
16447                 this.el.dom.value = d.innerHTML;
16448                 this.owner.fireEvent('push', this, v);
16449             }
16450         }
16451     },
16452
16453     // private
16454     deferFocus : function(){
16455         this.focus.defer(10, this);
16456     },
16457
16458     // doc'ed in Field
16459     focus : function(){
16460         if(this.win && !this.sourceEditMode){
16461             this.win.focus();
16462         }else{
16463             this.el.focus();
16464         }
16465     },
16466     
16467     assignDocWin: function()
16468     {
16469         var iframe = this.iframe;
16470         
16471          if(Roo.isIE){
16472             this.doc = iframe.contentWindow.document;
16473             this.win = iframe.contentWindow;
16474         } else {
16475             if (!Roo.get(this.frameId)) {
16476                 return;
16477             }
16478             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
16479             this.win = Roo.get(this.frameId).dom.contentWindow;
16480         }
16481     },
16482     
16483     // private
16484     initEditor : function(){
16485         //console.log("INIT EDITOR");
16486         this.assignDocWin();
16487         
16488         
16489         
16490         this.doc.designMode="on";
16491         this.doc.open();
16492         this.doc.write(this.getDocMarkup());
16493         this.doc.close();
16494         
16495         var dbody = (this.doc.body || this.doc.documentElement);
16496         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
16497         // this copies styles from the containing element into thsi one..
16498         // not sure why we need all of this..
16499         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
16500         
16501         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
16502         //ss['background-attachment'] = 'fixed'; // w3c
16503         dbody.bgProperties = 'fixed'; // ie
16504         //Roo.DomHelper.applyStyles(dbody, ss);
16505         Roo.EventManager.on(this.doc, {
16506             //'mousedown': this.onEditorEvent,
16507             'mouseup': this.onEditorEvent,
16508             'dblclick': this.onEditorEvent,
16509             'click': this.onEditorEvent,
16510             'keyup': this.onEditorEvent,
16511             buffer:100,
16512             scope: this
16513         });
16514         if(Roo.isGecko){
16515             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
16516         }
16517         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
16518             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
16519         }
16520         this.initialized = true;
16521
16522         this.owner.fireEvent('initialize', this);
16523         this.pushValue();
16524     },
16525
16526     // private
16527     onDestroy : function(){
16528         
16529         
16530         
16531         if(this.rendered){
16532             
16533             //for (var i =0; i < this.toolbars.length;i++) {
16534             //    // fixme - ask toolbars for heights?
16535             //    this.toolbars[i].onDestroy();
16536            // }
16537             
16538             //this.wrap.dom.innerHTML = '';
16539             //this.wrap.remove();
16540         }
16541     },
16542
16543     // private
16544     onFirstFocus : function(){
16545         
16546         this.assignDocWin();
16547         
16548         
16549         this.activated = true;
16550          
16551     
16552         if(Roo.isGecko){ // prevent silly gecko errors
16553             this.win.focus();
16554             var s = this.win.getSelection();
16555             if(!s.focusNode || s.focusNode.nodeType != 3){
16556                 var r = s.getRangeAt(0);
16557                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
16558                 r.collapse(true);
16559                 this.deferFocus();
16560             }
16561             try{
16562                 this.execCmd('useCSS', true);
16563                 this.execCmd('styleWithCSS', false);
16564             }catch(e){}
16565         }
16566         this.owner.fireEvent('activate', this);
16567     },
16568
16569     // private
16570     adjustFont: function(btn){
16571         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
16572         //if(Roo.isSafari){ // safari
16573         //    adjust *= 2;
16574        // }
16575         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
16576         if(Roo.isSafari){ // safari
16577             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
16578             v =  (v < 10) ? 10 : v;
16579             v =  (v > 48) ? 48 : v;
16580             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
16581             
16582         }
16583         
16584         
16585         v = Math.max(1, v+adjust);
16586         
16587         this.execCmd('FontSize', v  );
16588     },
16589
16590     onEditorEvent : function(e){
16591         this.owner.fireEvent('editorevent', this, e);
16592       //  this.updateToolbar();
16593         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
16594     },
16595
16596     insertTag : function(tg)
16597     {
16598         // could be a bit smarter... -> wrap the current selected tRoo..
16599         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
16600             
16601             range = this.createRange(this.getSelection());
16602             var wrappingNode = this.doc.createElement(tg.toLowerCase());
16603             wrappingNode.appendChild(range.extractContents());
16604             range.insertNode(wrappingNode);
16605
16606             return;
16607             
16608             
16609             
16610         }
16611         this.execCmd("formatblock",   tg);
16612         
16613     },
16614     
16615     insertText : function(txt)
16616     {
16617         
16618         
16619         var range = this.createRange();
16620         range.deleteContents();
16621                //alert(Sender.getAttribute('label'));
16622                
16623         range.insertNode(this.doc.createTextNode(txt));
16624     } ,
16625     
16626      
16627
16628     /**
16629      * Executes a Midas editor command on the editor document and performs necessary focus and
16630      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
16631      * @param {String} cmd The Midas command
16632      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16633      */
16634     relayCmd : function(cmd, value){
16635         this.win.focus();
16636         this.execCmd(cmd, value);
16637         this.owner.fireEvent('editorevent', this);
16638         //this.updateToolbar();
16639         this.owner.deferFocus();
16640     },
16641
16642     /**
16643      * Executes a Midas editor command directly on the editor document.
16644      * For visual commands, you should use {@link #relayCmd} instead.
16645      * <b>This should only be called after the editor is initialized.</b>
16646      * @param {String} cmd The Midas command
16647      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16648      */
16649     execCmd : function(cmd, value){
16650         this.doc.execCommand(cmd, false, value === undefined ? null : value);
16651         this.syncValue();
16652     },
16653  
16654  
16655    
16656     /**
16657      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
16658      * to insert tRoo.
16659      * @param {String} text | dom node.. 
16660      */
16661     insertAtCursor : function(text)
16662     {
16663         
16664         
16665         
16666         if(!this.activated){
16667             return;
16668         }
16669         /*
16670         if(Roo.isIE){
16671             this.win.focus();
16672             var r = this.doc.selection.createRange();
16673             if(r){
16674                 r.collapse(true);
16675                 r.pasteHTML(text);
16676                 this.syncValue();
16677                 this.deferFocus();
16678             
16679             }
16680             return;
16681         }
16682         */
16683         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
16684             this.win.focus();
16685             
16686             
16687             // from jquery ui (MIT licenced)
16688             var range, node;
16689             var win = this.win;
16690             
16691             if (win.getSelection && win.getSelection().getRangeAt) {
16692                 range = win.getSelection().getRangeAt(0);
16693                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
16694                 range.insertNode(node);
16695             } else if (win.document.selection && win.document.selection.createRange) {
16696                 // no firefox support
16697                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
16698                 win.document.selection.createRange().pasteHTML(txt);
16699             } else {
16700                 // no firefox support
16701                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
16702                 this.execCmd('InsertHTML', txt);
16703             } 
16704             
16705             this.syncValue();
16706             
16707             this.deferFocus();
16708         }
16709     },
16710  // private
16711     mozKeyPress : function(e){
16712         if(e.ctrlKey){
16713             var c = e.getCharCode(), cmd;
16714           
16715             if(c > 0){
16716                 c = String.fromCharCode(c).toLowerCase();
16717                 switch(c){
16718                     case 'b':
16719                         cmd = 'bold';
16720                         break;
16721                     case 'i':
16722                         cmd = 'italic';
16723                         break;
16724                     
16725                     case 'u':
16726                         cmd = 'underline';
16727                         break;
16728                     
16729                     case 'v':
16730                         this.cleanUpPaste.defer(100, this);
16731                         return;
16732                         
16733                 }
16734                 if(cmd){
16735                     this.win.focus();
16736                     this.execCmd(cmd);
16737                     this.deferFocus();
16738                     e.preventDefault();
16739                 }
16740                 
16741             }
16742         }
16743     },
16744
16745     // private
16746     fixKeys : function(){ // load time branching for fastest keydown performance
16747         if(Roo.isIE){
16748             return function(e){
16749                 var k = e.getKey(), r;
16750                 if(k == e.TAB){
16751                     e.stopEvent();
16752                     r = this.doc.selection.createRange();
16753                     if(r){
16754                         r.collapse(true);
16755                         r.pasteHTML('&#160;&#160;&#160;&#160;');
16756                         this.deferFocus();
16757                     }
16758                     return;
16759                 }
16760                 
16761                 if(k == e.ENTER){
16762                     r = this.doc.selection.createRange();
16763                     if(r){
16764                         var target = r.parentElement();
16765                         if(!target || target.tagName.toLowerCase() != 'li'){
16766                             e.stopEvent();
16767                             r.pasteHTML('<br />');
16768                             r.collapse(false);
16769                             r.select();
16770                         }
16771                     }
16772                 }
16773                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
16774                     this.cleanUpPaste.defer(100, this);
16775                     return;
16776                 }
16777                 
16778                 
16779             };
16780         }else if(Roo.isOpera){
16781             return function(e){
16782                 var k = e.getKey();
16783                 if(k == e.TAB){
16784                     e.stopEvent();
16785                     this.win.focus();
16786                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
16787                     this.deferFocus();
16788                 }
16789                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
16790                     this.cleanUpPaste.defer(100, this);
16791                     return;
16792                 }
16793                 
16794             };
16795         }else if(Roo.isSafari){
16796             return function(e){
16797                 var k = e.getKey();
16798                 
16799                 if(k == e.TAB){
16800                     e.stopEvent();
16801                     this.execCmd('InsertText','\t');
16802                     this.deferFocus();
16803                     return;
16804                 }
16805                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
16806                     this.cleanUpPaste.defer(100, this);
16807                     return;
16808                 }
16809                 
16810              };
16811         }
16812     }(),
16813     
16814     getAllAncestors: function()
16815     {
16816         var p = this.getSelectedNode();
16817         var a = [];
16818         if (!p) {
16819             a.push(p); // push blank onto stack..
16820             p = this.getParentElement();
16821         }
16822         
16823         
16824         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
16825             a.push(p);
16826             p = p.parentNode;
16827         }
16828         a.push(this.doc.body);
16829         return a;
16830     },
16831     lastSel : false,
16832     lastSelNode : false,
16833     
16834     
16835     getSelection : function() 
16836     {
16837         this.assignDocWin();
16838         return Roo.isIE ? this.doc.selection : this.win.getSelection();
16839     },
16840     
16841     getSelectedNode: function() 
16842     {
16843         // this may only work on Gecko!!!
16844         
16845         // should we cache this!!!!
16846         
16847         
16848         
16849          
16850         var range = this.createRange(this.getSelection()).cloneRange();
16851         
16852         if (Roo.isIE) {
16853             var parent = range.parentElement();
16854             while (true) {
16855                 var testRange = range.duplicate();
16856                 testRange.moveToElementText(parent);
16857                 if (testRange.inRange(range)) {
16858                     break;
16859                 }
16860                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
16861                     break;
16862                 }
16863                 parent = parent.parentElement;
16864             }
16865             return parent;
16866         }
16867         
16868         // is ancestor a text element.
16869         var ac =  range.commonAncestorContainer;
16870         if (ac.nodeType == 3) {
16871             ac = ac.parentNode;
16872         }
16873         
16874         var ar = ac.childNodes;
16875          
16876         var nodes = [];
16877         var other_nodes = [];
16878         var has_other_nodes = false;
16879         for (var i=0;i<ar.length;i++) {
16880             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
16881                 continue;
16882             }
16883             // fullly contained node.
16884             
16885             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
16886                 nodes.push(ar[i]);
16887                 continue;
16888             }
16889             
16890             // probably selected..
16891             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
16892                 other_nodes.push(ar[i]);
16893                 continue;
16894             }
16895             // outer..
16896             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
16897                 continue;
16898             }
16899             
16900             
16901             has_other_nodes = true;
16902         }
16903         if (!nodes.length && other_nodes.length) {
16904             nodes= other_nodes;
16905         }
16906         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
16907             return false;
16908         }
16909         
16910         return nodes[0];
16911     },
16912     createRange: function(sel)
16913     {
16914         // this has strange effects when using with 
16915         // top toolbar - not sure if it's a great idea.
16916         //this.editor.contentWindow.focus();
16917         if (typeof sel != "undefined") {
16918             try {
16919                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
16920             } catch(e) {
16921                 return this.doc.createRange();
16922             }
16923         } else {
16924             return this.doc.createRange();
16925         }
16926     },
16927     getParentElement: function()
16928     {
16929         
16930         this.assignDocWin();
16931         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
16932         
16933         var range = this.createRange(sel);
16934          
16935         try {
16936             var p = range.commonAncestorContainer;
16937             while (p.nodeType == 3) { // text node
16938                 p = p.parentNode;
16939             }
16940             return p;
16941         } catch (e) {
16942             return null;
16943         }
16944     
16945     },
16946     /***
16947      *
16948      * Range intersection.. the hard stuff...
16949      *  '-1' = before
16950      *  '0' = hits..
16951      *  '1' = after.
16952      *         [ -- selected range --- ]
16953      *   [fail]                        [fail]
16954      *
16955      *    basically..
16956      *      if end is before start or  hits it. fail.
16957      *      if start is after end or hits it fail.
16958      *
16959      *   if either hits (but other is outside. - then it's not 
16960      *   
16961      *    
16962      **/
16963     
16964     
16965     // @see http://www.thismuchiknow.co.uk/?p=64.
16966     rangeIntersectsNode : function(range, node)
16967     {
16968         var nodeRange = node.ownerDocument.createRange();
16969         try {
16970             nodeRange.selectNode(node);
16971         } catch (e) {
16972             nodeRange.selectNodeContents(node);
16973         }
16974     
16975         var rangeStartRange = range.cloneRange();
16976         rangeStartRange.collapse(true);
16977     
16978         var rangeEndRange = range.cloneRange();
16979         rangeEndRange.collapse(false);
16980     
16981         var nodeStartRange = nodeRange.cloneRange();
16982         nodeStartRange.collapse(true);
16983     
16984         var nodeEndRange = nodeRange.cloneRange();
16985         nodeEndRange.collapse(false);
16986     
16987         return rangeStartRange.compareBoundaryPoints(
16988                  Range.START_TO_START, nodeEndRange) == -1 &&
16989                rangeEndRange.compareBoundaryPoints(
16990                  Range.START_TO_START, nodeStartRange) == 1;
16991         
16992          
16993     },
16994     rangeCompareNode : function(range, node)
16995     {
16996         var nodeRange = node.ownerDocument.createRange();
16997         try {
16998             nodeRange.selectNode(node);
16999         } catch (e) {
17000             nodeRange.selectNodeContents(node);
17001         }
17002         
17003         
17004         range.collapse(true);
17005     
17006         nodeRange.collapse(true);
17007      
17008         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
17009         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
17010          
17011         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
17012         
17013         var nodeIsBefore   =  ss == 1;
17014         var nodeIsAfter    = ee == -1;
17015         
17016         if (nodeIsBefore && nodeIsAfter)
17017             return 0; // outer
17018         if (!nodeIsBefore && nodeIsAfter)
17019             return 1; //right trailed.
17020         
17021         if (nodeIsBefore && !nodeIsAfter)
17022             return 2;  // left trailed.
17023         // fully contined.
17024         return 3;
17025     },
17026
17027     // private? - in a new class?
17028     cleanUpPaste :  function()
17029     {
17030         // cleans up the whole document..
17031         Roo.log('cleanuppaste');
17032         
17033         this.cleanUpChildren(this.doc.body);
17034         var clean = this.cleanWordChars(this.doc.body.innerHTML);
17035         if (clean != this.doc.body.innerHTML) {
17036             this.doc.body.innerHTML = clean;
17037         }
17038         
17039     },
17040     
17041     cleanWordChars : function(input) {// change the chars to hex code
17042         var he = Roo.HtmlEditorCore;
17043         
17044         var output = input;
17045         Roo.each(he.swapCodes, function(sw) { 
17046             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
17047             
17048             output = output.replace(swapper, sw[1]);
17049         });
17050         
17051         return output;
17052     },
17053     
17054     
17055     cleanUpChildren : function (n)
17056     {
17057         if (!n.childNodes.length) {
17058             return;
17059         }
17060         for (var i = n.childNodes.length-1; i > -1 ; i--) {
17061            this.cleanUpChild(n.childNodes[i]);
17062         }
17063     },
17064     
17065     
17066         
17067     
17068     cleanUpChild : function (node)
17069     {
17070         var ed = this;
17071         //console.log(node);
17072         if (node.nodeName == "#text") {
17073             // clean up silly Windows -- stuff?
17074             return; 
17075         }
17076         if (node.nodeName == "#comment") {
17077             node.parentNode.removeChild(node);
17078             // clean up silly Windows -- stuff?
17079             return; 
17080         }
17081         
17082         if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1 && this.clearUp) {
17083             // remove node.
17084             node.parentNode.removeChild(node);
17085             return;
17086             
17087         }
17088         
17089         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
17090         
17091         // remove <a name=....> as rendering on yahoo mailer is borked with this.
17092         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
17093         
17094         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
17095         //    remove_keep_children = true;
17096         //}
17097         
17098         if (remove_keep_children) {
17099             this.cleanUpChildren(node);
17100             // inserts everything just before this node...
17101             while (node.childNodes.length) {
17102                 var cn = node.childNodes[0];
17103                 node.removeChild(cn);
17104                 node.parentNode.insertBefore(cn, node);
17105             }
17106             node.parentNode.removeChild(node);
17107             return;
17108         }
17109         
17110         if (!node.attributes || !node.attributes.length) {
17111             this.cleanUpChildren(node);
17112             return;
17113         }
17114         
17115         function cleanAttr(n,v)
17116         {
17117             
17118             if (v.match(/^\./) || v.match(/^\//)) {
17119                 return;
17120             }
17121             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
17122                 return;
17123             }
17124             if (v.match(/^#/)) {
17125                 return;
17126             }
17127 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
17128             node.removeAttribute(n);
17129             
17130         }
17131         
17132         function cleanStyle(n,v)
17133         {
17134             if (v.match(/expression/)) { //XSS?? should we even bother..
17135                 node.removeAttribute(n);
17136                 return;
17137             }
17138             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
17139             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
17140             
17141             
17142             var parts = v.split(/;/);
17143             var clean = [];
17144             
17145             Roo.each(parts, function(p) {
17146                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
17147                 if (!p.length) {
17148                     return true;
17149                 }
17150                 var l = p.split(':').shift().replace(/\s+/g,'');
17151                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
17152                 
17153                 if ( cblack.indexOf(l) > -1) {
17154 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17155                     //node.removeAttribute(n);
17156                     return true;
17157                 }
17158                 //Roo.log()
17159                 // only allow 'c whitelisted system attributes'
17160                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
17161 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17162                     //node.removeAttribute(n);
17163                     return true;
17164                 }
17165                 
17166                 
17167                  
17168                 
17169                 clean.push(p);
17170                 return true;
17171             });
17172             if (clean.length) { 
17173                 node.setAttribute(n, clean.join(';'));
17174             } else {
17175                 node.removeAttribute(n);
17176             }
17177             
17178         }
17179         
17180         
17181         for (var i = node.attributes.length-1; i > -1 ; i--) {
17182             var a = node.attributes[i];
17183             //console.log(a);
17184             
17185             if (a.name.toLowerCase().substr(0,2)=='on')  {
17186                 node.removeAttribute(a.name);
17187                 continue;
17188             }
17189             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
17190                 node.removeAttribute(a.name);
17191                 continue;
17192             }
17193             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
17194                 cleanAttr(a.name,a.value); // fixme..
17195                 continue;
17196             }
17197             if (a.name == 'style') {
17198                 cleanStyle(a.name,a.value);
17199                 continue;
17200             }
17201             /// clean up MS crap..
17202             // tecnically this should be a list of valid class'es..
17203             
17204             
17205             if (a.name == 'class') {
17206                 if (a.value.match(/^Mso/)) {
17207                     node.className = '';
17208                 }
17209                 
17210                 if (a.value.match(/body/)) {
17211                     node.className = '';
17212                 }
17213                 continue;
17214             }
17215             
17216             // style cleanup!?
17217             // class cleanup?
17218             
17219         }
17220         
17221         
17222         this.cleanUpChildren(node);
17223         
17224         
17225     },
17226     /**
17227      * Clean up MS wordisms...
17228      */
17229     cleanWord : function(node)
17230     {
17231         var _t = this;
17232         var cleanWordChildren = function()
17233         {
17234             if (!node.childNodes.length) {
17235                 return;
17236             }
17237             for (var i = node.childNodes.length-1; i > -1 ; i--) {
17238                _t.cleanWord(node.childNodes[i]);
17239             }
17240         }
17241         
17242         
17243         if (!node) {
17244             this.cleanWord(this.doc.body);
17245             return;
17246         }
17247         if (node.nodeName == "#text") {
17248             // clean up silly Windows -- stuff?
17249             return; 
17250         }
17251         if (node.nodeName == "#comment") {
17252             node.parentNode.removeChild(node);
17253             // clean up silly Windows -- stuff?
17254             return; 
17255         }
17256         
17257         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
17258             node.parentNode.removeChild(node);
17259             return;
17260         }
17261         
17262         // remove - but keep children..
17263         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
17264             while (node.childNodes.length) {
17265                 var cn = node.childNodes[0];
17266                 node.removeChild(cn);
17267                 node.parentNode.insertBefore(cn, node);
17268             }
17269             node.parentNode.removeChild(node);
17270             cleanWordChildren();
17271             return;
17272         }
17273         // clean styles
17274         if (node.className.length) {
17275             
17276             var cn = node.className.split(/\W+/);
17277             var cna = [];
17278             Roo.each(cn, function(cls) {
17279                 if (cls.match(/Mso[a-zA-Z]+/)) {
17280                     return;
17281                 }
17282                 cna.push(cls);
17283             });
17284             node.className = cna.length ? cna.join(' ') : '';
17285             if (!cna.length) {
17286                 node.removeAttribute("class");
17287             }
17288         }
17289         
17290         if (node.hasAttribute("lang")) {
17291             node.removeAttribute("lang");
17292         }
17293         
17294         if (node.hasAttribute("style")) {
17295             
17296             var styles = node.getAttribute("style").split(";");
17297             var nstyle = [];
17298             Roo.each(styles, function(s) {
17299                 if (!s.match(/:/)) {
17300                     return;
17301                 }
17302                 var kv = s.split(":");
17303                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
17304                     return;
17305                 }
17306                 // what ever is left... we allow.
17307                 nstyle.push(s);
17308             });
17309             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
17310             if (!nstyle.length) {
17311                 node.removeAttribute('style');
17312             }
17313         }
17314         
17315         cleanWordChildren();
17316         
17317         
17318     },
17319     domToHTML : function(currentElement, depth, nopadtext) {
17320         
17321             depth = depth || 0;
17322             nopadtext = nopadtext || false;
17323         
17324             if (!currentElement) {
17325                 return this.domToHTML(this.doc.body);
17326             }
17327             
17328             //Roo.log(currentElement);
17329             var j;
17330             var allText = false;
17331             var nodeName = currentElement.nodeName;
17332             var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
17333             
17334             if  (nodeName == '#text') {
17335                 return currentElement.nodeValue;
17336             }
17337             
17338             
17339             var ret = '';
17340             if (nodeName != 'BODY') {
17341                  
17342                 var i = 0;
17343                 // Prints the node tagName, such as <A>, <IMG>, etc
17344                 if (tagName) {
17345                     var attr = [];
17346                     for(i = 0; i < currentElement.attributes.length;i++) {
17347                         // quoting?
17348                         var aname = currentElement.attributes.item(i).name;
17349                         if (!currentElement.attributes.item(i).value.length) {
17350                             continue;
17351                         }
17352                         attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
17353                     }
17354                     
17355                     ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
17356                 } 
17357                 else {
17358                     
17359                     // eack
17360                 }
17361             } else {
17362                 tagName = false;
17363             }
17364             if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
17365                 return ret;
17366             }
17367             if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
17368                 nopadtext = true;
17369             }
17370             
17371             
17372             // Traverse the tree
17373             i = 0;
17374             var currentElementChild = currentElement.childNodes.item(i);
17375             var allText = true;
17376             var innerHTML  = '';
17377             lastnode = '';
17378             while (currentElementChild) {
17379                 // Formatting code (indent the tree so it looks nice on the screen)
17380                 var nopad = nopadtext;
17381                 if (lastnode == 'SPAN') {
17382                     nopad  = true;
17383                 }
17384                 // text
17385                 if  (currentElementChild.nodeName == '#text') {
17386                     var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
17387                     if (!nopad && toadd.length > 80) {
17388                         innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
17389                     }
17390                     innerHTML  += toadd;
17391                     
17392                     i++;
17393                     currentElementChild = currentElement.childNodes.item(i);
17394                     lastNode = '';
17395                     continue;
17396                 }
17397                 allText = false;
17398                 
17399                 innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
17400                     
17401                 // Recursively traverse the tree structure of the child node
17402                 innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
17403                 lastnode = currentElementChild.nodeName;
17404                 i++;
17405                 currentElementChild=currentElement.childNodes.item(i);
17406             }
17407             
17408             ret += innerHTML;
17409             
17410             if (!allText) {
17411                     // The remaining code is mostly for formatting the tree
17412                 ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
17413             }
17414             
17415             
17416             if (tagName) {
17417                 ret+= "</"+tagName+">";
17418             }
17419             return ret;
17420             
17421         }
17422     
17423     // hide stuff that is not compatible
17424     /**
17425      * @event blur
17426      * @hide
17427      */
17428     /**
17429      * @event change
17430      * @hide
17431      */
17432     /**
17433      * @event focus
17434      * @hide
17435      */
17436     /**
17437      * @event specialkey
17438      * @hide
17439      */
17440     /**
17441      * @cfg {String} fieldClass @hide
17442      */
17443     /**
17444      * @cfg {String} focusClass @hide
17445      */
17446     /**
17447      * @cfg {String} autoCreate @hide
17448      */
17449     /**
17450      * @cfg {String} inputType @hide
17451      */
17452     /**
17453      * @cfg {String} invalidClass @hide
17454      */
17455     /**
17456      * @cfg {String} invalidText @hide
17457      */
17458     /**
17459      * @cfg {String} msgFx @hide
17460      */
17461     /**
17462      * @cfg {String} validateOnBlur @hide
17463      */
17464 });
17465
17466 Roo.HtmlEditorCore.white = [
17467         'area', 'br', 'img', 'input', 'hr', 'wbr',
17468         
17469        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
17470        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
17471        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
17472        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
17473        'table',   'ul',         'xmp', 
17474        
17475        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
17476       'thead',   'tr', 
17477      
17478       'dir', 'menu', 'ol', 'ul', 'dl',
17479        
17480       'embed',  'object'
17481 ];
17482
17483
17484 Roo.HtmlEditorCore.black = [
17485     //    'embed',  'object', // enable - backend responsiblity to clean thiese
17486         'applet', // 
17487         'base',   'basefont', 'bgsound', 'blink',  'body', 
17488         'frame',  'frameset', 'head',    'html',   'ilayer', 
17489         'iframe', 'layer',  'link',     'meta',    'object',   
17490         'script', 'style' ,'title',  'xml' // clean later..
17491 ];
17492 Roo.HtmlEditorCore.clean = [
17493     'script', 'style', 'title', 'xml'
17494 ];
17495 Roo.HtmlEditorCore.remove = [
17496     'font'
17497 ];
17498 // attributes..
17499
17500 Roo.HtmlEditorCore.ablack = [
17501     'on'
17502 ];
17503     
17504 Roo.HtmlEditorCore.aclean = [ 
17505     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
17506 ];
17507
17508 // protocols..
17509 Roo.HtmlEditorCore.pwhite= [
17510         'http',  'https',  'mailto'
17511 ];
17512
17513 // white listed style attributes.
17514 Roo.HtmlEditorCore.cwhite= [
17515       //  'text-align', /// default is to allow most things..
17516       
17517          
17518 //        'font-size'//??
17519 ];
17520
17521 // black listed style attributes.
17522 Roo.HtmlEditorCore.cblack= [
17523       //  'font-size' -- this can be set by the project 
17524 ];
17525
17526
17527 Roo.HtmlEditorCore.swapCodes   =[ 
17528     [    8211, "--" ], 
17529     [    8212, "--" ], 
17530     [    8216,  "'" ],  
17531     [    8217, "'" ],  
17532     [    8220, '"' ],  
17533     [    8221, '"' ],  
17534     [    8226, "*" ],  
17535     [    8230, "..." ]
17536 ]; 
17537
17538     /*
17539  * - LGPL
17540  *
17541  * HtmlEditor
17542  * 
17543  */
17544
17545 /**
17546  * @class Roo.bootstrap.HtmlEditor
17547  * @extends Roo.bootstrap.TextArea
17548  * Bootstrap HtmlEditor class
17549
17550  * @constructor
17551  * Create a new HtmlEditor
17552  * @param {Object} config The config object
17553  */
17554
17555 Roo.bootstrap.HtmlEditor = function(config){
17556     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
17557     if (!this.toolbars) {
17558         this.toolbars = [];
17559     }
17560     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
17561     this.addEvents({
17562             /**
17563              * @event initialize
17564              * Fires when the editor is fully initialized (including the iframe)
17565              * @param {HtmlEditor} this
17566              */
17567             initialize: true,
17568             /**
17569              * @event activate
17570              * Fires when the editor is first receives the focus. Any insertion must wait
17571              * until after this event.
17572              * @param {HtmlEditor} this
17573              */
17574             activate: true,
17575              /**
17576              * @event beforesync
17577              * Fires before the textarea is updated with content from the editor iframe. Return false
17578              * to cancel the sync.
17579              * @param {HtmlEditor} this
17580              * @param {String} html
17581              */
17582             beforesync: true,
17583              /**
17584              * @event beforepush
17585              * Fires before the iframe editor is updated with content from the textarea. Return false
17586              * to cancel the push.
17587              * @param {HtmlEditor} this
17588              * @param {String} html
17589              */
17590             beforepush: true,
17591              /**
17592              * @event sync
17593              * Fires when the textarea is updated with content from the editor iframe.
17594              * @param {HtmlEditor} this
17595              * @param {String} html
17596              */
17597             sync: true,
17598              /**
17599              * @event push
17600              * Fires when the iframe editor is updated with content from the textarea.
17601              * @param {HtmlEditor} this
17602              * @param {String} html
17603              */
17604             push: true,
17605              /**
17606              * @event editmodechange
17607              * Fires when the editor switches edit modes
17608              * @param {HtmlEditor} this
17609              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
17610              */
17611             editmodechange: true,
17612             /**
17613              * @event editorevent
17614              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
17615              * @param {HtmlEditor} this
17616              */
17617             editorevent: true,
17618             /**
17619              * @event firstfocus
17620              * Fires when on first focus - needed by toolbars..
17621              * @param {HtmlEditor} this
17622              */
17623             firstfocus: true,
17624             /**
17625              * @event autosave
17626              * Auto save the htmlEditor value as a file into Events
17627              * @param {HtmlEditor} this
17628              */
17629             autosave: true,
17630             /**
17631              * @event savedpreview
17632              * preview the saved version of htmlEditor
17633              * @param {HtmlEditor} this
17634              */
17635             savedpreview: true
17636         });
17637 };
17638
17639
17640 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
17641     
17642     
17643       /**
17644      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
17645      */
17646     toolbars : false,
17647    
17648      /**
17649      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
17650      *                        Roo.resizable.
17651      */
17652     resizable : false,
17653      /**
17654      * @cfg {Number} height (in pixels)
17655      */   
17656     height: 300,
17657    /**
17658      * @cfg {Number} width (in pixels)
17659      */   
17660     width: false,
17661     
17662     /**
17663      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
17664      * 
17665      */
17666     stylesheets: false,
17667     
17668     // id of frame..
17669     frameId: false,
17670     
17671     // private properties
17672     validationEvent : false,
17673     deferHeight: true,
17674     initialized : false,
17675     activated : false,
17676     
17677     onFocus : Roo.emptyFn,
17678     iframePad:3,
17679     hideMode:'offsets',
17680     
17681     
17682     tbContainer : false,
17683     
17684     toolbarContainer :function() {
17685         return this.wrap.select('.x-html-editor-tb',true).first();
17686     },
17687
17688     /**
17689      * Protected method that will not generally be called directly. It
17690      * is called when the editor creates its toolbar. Override this method if you need to
17691      * add custom toolbar buttons.
17692      * @param {HtmlEditor} editor
17693      */
17694     createToolbar : function(){
17695         
17696         Roo.log("create toolbars");
17697         
17698         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
17699         this.toolbars[0].render(this.toolbarContainer());
17700         
17701         return;
17702         
17703 //        if (!editor.toolbars || !editor.toolbars.length) {
17704 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
17705 //        }
17706 //        
17707 //        for (var i =0 ; i < editor.toolbars.length;i++) {
17708 //            editor.toolbars[i] = Roo.factory(
17709 //                    typeof(editor.toolbars[i]) == 'string' ?
17710 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
17711 //                Roo.bootstrap.HtmlEditor);
17712 //            editor.toolbars[i].init(editor);
17713 //        }
17714     },
17715
17716      
17717     // private
17718     onRender : function(ct, position)
17719     {
17720        // Roo.log("Call onRender: " + this.xtype);
17721         var _t = this;
17722         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
17723       
17724         this.wrap = this.inputEl().wrap({
17725             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
17726         });
17727         
17728         this.editorcore.onRender(ct, position);
17729          
17730         if (this.resizable) {
17731             this.resizeEl = new Roo.Resizable(this.wrap, {
17732                 pinned : true,
17733                 wrap: true,
17734                 dynamic : true,
17735                 minHeight : this.height,
17736                 height: this.height,
17737                 handles : this.resizable,
17738                 width: this.width,
17739                 listeners : {
17740                     resize : function(r, w, h) {
17741                         _t.onResize(w,h); // -something
17742                     }
17743                 }
17744             });
17745             
17746         }
17747         this.createToolbar(this);
17748        
17749         
17750         if(!this.width && this.resizable){
17751             this.setSize(this.wrap.getSize());
17752         }
17753         if (this.resizeEl) {
17754             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
17755             // should trigger onReize..
17756         }
17757         
17758     },
17759
17760     // private
17761     onResize : function(w, h)
17762     {
17763         Roo.log('resize: ' +w + ',' + h );
17764         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
17765         var ew = false;
17766         var eh = false;
17767         
17768         if(this.inputEl() ){
17769             if(typeof w == 'number'){
17770                 var aw = w - this.wrap.getFrameWidth('lr');
17771                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
17772                 ew = aw;
17773             }
17774             if(typeof h == 'number'){
17775                  var tbh = -11;  // fixme it needs to tool bar size!
17776                 for (var i =0; i < this.toolbars.length;i++) {
17777                     // fixme - ask toolbars for heights?
17778                     tbh += this.toolbars[i].el.getHeight();
17779                     //if (this.toolbars[i].footer) {
17780                     //    tbh += this.toolbars[i].footer.el.getHeight();
17781                     //}
17782                 }
17783               
17784                 
17785                 
17786                 
17787                 
17788                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
17789                 ah -= 5; // knock a few pixes off for look..
17790                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
17791                 var eh = ah;
17792             }
17793         }
17794         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
17795         this.editorcore.onResize(ew,eh);
17796         
17797     },
17798
17799     /**
17800      * Toggles the editor between standard and source edit mode.
17801      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
17802      */
17803     toggleSourceEdit : function(sourceEditMode)
17804     {
17805         this.editorcore.toggleSourceEdit(sourceEditMode);
17806         
17807         if(this.editorcore.sourceEditMode){
17808             Roo.log('editor - showing textarea');
17809             
17810 //            Roo.log('in');
17811 //            Roo.log(this.syncValue());
17812             this.syncValue();
17813             this.inputEl().removeClass(['hide', 'x-hidden']);
17814             this.inputEl().dom.removeAttribute('tabIndex');
17815             this.inputEl().focus();
17816         }else{
17817             Roo.log('editor - hiding textarea');
17818 //            Roo.log('out')
17819 //            Roo.log(this.pushValue()); 
17820             this.pushValue();
17821             
17822             this.inputEl().addClass(['hide', 'x-hidden']);
17823             this.inputEl().dom.setAttribute('tabIndex', -1);
17824             //this.deferFocus();
17825         }
17826          
17827         if(this.resizable){
17828             this.setSize(this.wrap.getSize());
17829         }
17830         
17831         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
17832     },
17833  
17834     // private (for BoxComponent)
17835     adjustSize : Roo.BoxComponent.prototype.adjustSize,
17836
17837     // private (for BoxComponent)
17838     getResizeEl : function(){
17839         return this.wrap;
17840     },
17841
17842     // private (for BoxComponent)
17843     getPositionEl : function(){
17844         return this.wrap;
17845     },
17846
17847     // private
17848     initEvents : function(){
17849         this.originalValue = this.getValue();
17850     },
17851
17852 //    /**
17853 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
17854 //     * @method
17855 //     */
17856 //    markInvalid : Roo.emptyFn,
17857 //    /**
17858 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
17859 //     * @method
17860 //     */
17861 //    clearInvalid : Roo.emptyFn,
17862
17863     setValue : function(v){
17864         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
17865         this.editorcore.pushValue();
17866     },
17867
17868      
17869     // private
17870     deferFocus : function(){
17871         this.focus.defer(10, this);
17872     },
17873
17874     // doc'ed in Field
17875     focus : function(){
17876         this.editorcore.focus();
17877         
17878     },
17879       
17880
17881     // private
17882     onDestroy : function(){
17883         
17884         
17885         
17886         if(this.rendered){
17887             
17888             for (var i =0; i < this.toolbars.length;i++) {
17889                 // fixme - ask toolbars for heights?
17890                 this.toolbars[i].onDestroy();
17891             }
17892             
17893             this.wrap.dom.innerHTML = '';
17894             this.wrap.remove();
17895         }
17896     },
17897
17898     // private
17899     onFirstFocus : function(){
17900         //Roo.log("onFirstFocus");
17901         this.editorcore.onFirstFocus();
17902          for (var i =0; i < this.toolbars.length;i++) {
17903             this.toolbars[i].onFirstFocus();
17904         }
17905         
17906     },
17907     
17908     // private
17909     syncValue : function()
17910     {   
17911         this.editorcore.syncValue();
17912     },
17913     
17914     pushValue : function()
17915     {   
17916         this.editorcore.pushValue();
17917     }
17918      
17919     
17920     // hide stuff that is not compatible
17921     /**
17922      * @event blur
17923      * @hide
17924      */
17925     /**
17926      * @event change
17927      * @hide
17928      */
17929     /**
17930      * @event focus
17931      * @hide
17932      */
17933     /**
17934      * @event specialkey
17935      * @hide
17936      */
17937     /**
17938      * @cfg {String} fieldClass @hide
17939      */
17940     /**
17941      * @cfg {String} focusClass @hide
17942      */
17943     /**
17944      * @cfg {String} autoCreate @hide
17945      */
17946     /**
17947      * @cfg {String} inputType @hide
17948      */
17949     /**
17950      * @cfg {String} invalidClass @hide
17951      */
17952     /**
17953      * @cfg {String} invalidText @hide
17954      */
17955     /**
17956      * @cfg {String} msgFx @hide
17957      */
17958     /**
17959      * @cfg {String} validateOnBlur @hide
17960      */
17961 });
17962  
17963     
17964    
17965    
17966    
17967       
17968 Roo.namespace('Roo.bootstrap.htmleditor');
17969 /**
17970  * @class Roo.bootstrap.HtmlEditorToolbar1
17971  * Basic Toolbar
17972  * 
17973  * Usage:
17974  *
17975  new Roo.bootstrap.HtmlEditor({
17976     ....
17977     toolbars : [
17978         new Roo.bootstrap.HtmlEditorToolbar1({
17979             disable : { fonts: 1 , format: 1, ..., ... , ...],
17980             btns : [ .... ]
17981         })
17982     }
17983      
17984  * 
17985  * @cfg {Object} disable List of elements to disable..
17986  * @cfg {Array} btns List of additional buttons.
17987  * 
17988  * 
17989  * NEEDS Extra CSS? 
17990  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
17991  */
17992  
17993 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
17994 {
17995     
17996     Roo.apply(this, config);
17997     
17998     // default disabled, based on 'good practice'..
17999     this.disable = this.disable || {};
18000     Roo.applyIf(this.disable, {
18001         fontSize : true,
18002         colors : true,
18003         specialElements : true
18004     });
18005     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
18006     
18007     this.editor = config.editor;
18008     this.editorcore = config.editor.editorcore;
18009     
18010     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
18011     
18012     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
18013     // dont call parent... till later.
18014 }
18015 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
18016      
18017     bar : true,
18018     
18019     editor : false,
18020     editorcore : false,
18021     
18022     
18023     formats : [
18024         "p" ,  
18025         "h1","h2","h3","h4","h5","h6", 
18026         "pre", "code", 
18027         "abbr", "acronym", "address", "cite", "samp", "var",
18028         'div','span'
18029     ],
18030     
18031     onRender : function(ct, position)
18032     {
18033        // Roo.log("Call onRender: " + this.xtype);
18034         
18035        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
18036        Roo.log(this.el);
18037        this.el.dom.style.marginBottom = '0';
18038        var _this = this;
18039        var editorcore = this.editorcore;
18040        var editor= this.editor;
18041        
18042        var children = [];
18043        var btn = function(id,cmd , toggle, handler){
18044        
18045             var  event = toggle ? 'toggle' : 'click';
18046        
18047             var a = {
18048                 size : 'sm',
18049                 xtype: 'Button',
18050                 xns: Roo.bootstrap,
18051                 glyphicon : id,
18052                 cmd : id || cmd,
18053                 enableToggle:toggle !== false,
18054                 //html : 'submit'
18055                 pressed : toggle ? false : null,
18056                 listeners : {}
18057             }
18058             a.listeners[toggle ? 'toggle' : 'click'] = function() {
18059                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
18060             }
18061             children.push(a);
18062             return a;
18063        }
18064         
18065         var style = {
18066                 xtype: 'Button',
18067                 size : 'sm',
18068                 xns: Roo.bootstrap,
18069                 glyphicon : 'font',
18070                 //html : 'submit'
18071                 menu : {
18072                     xtype: 'Menu',
18073                     xns: Roo.bootstrap,
18074                     items:  []
18075                 }
18076         };
18077         Roo.each(this.formats, function(f) {
18078             style.menu.items.push({
18079                 xtype :'MenuItem',
18080                 xns: Roo.bootstrap,
18081                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
18082                 tagname : f,
18083                 listeners : {
18084                     click : function()
18085                     {
18086                         editorcore.insertTag(this.tagname);
18087                         editor.focus();
18088                     }
18089                 }
18090                 
18091             });
18092         });
18093          children.push(style);   
18094             
18095             
18096         btn('bold',false,true);
18097         btn('italic',false,true);
18098         btn('align-left', 'justifyleft',true);
18099         btn('align-center', 'justifycenter',true);
18100         btn('align-right' , 'justifyright',true);
18101         btn('link', false, false, function(btn) {
18102             //Roo.log("create link?");
18103             var url = prompt(this.createLinkText, this.defaultLinkValue);
18104             if(url && url != 'http:/'+'/'){
18105                 this.editorcore.relayCmd('createlink', url);
18106             }
18107         }),
18108         btn('list','insertunorderedlist',true);
18109         btn('pencil', false,true, function(btn){
18110                 Roo.log(this);
18111                 
18112                 this.toggleSourceEdit(btn.pressed);
18113         });
18114         /*
18115         var cog = {
18116                 xtype: 'Button',
18117                 size : 'sm',
18118                 xns: Roo.bootstrap,
18119                 glyphicon : 'cog',
18120                 //html : 'submit'
18121                 menu : {
18122                     xtype: 'Menu',
18123                     xns: Roo.bootstrap,
18124                     items:  []
18125                 }
18126         };
18127         
18128         cog.menu.items.push({
18129             xtype :'MenuItem',
18130             xns: Roo.bootstrap,
18131             html : Clean styles,
18132             tagname : f,
18133             listeners : {
18134                 click : function()
18135                 {
18136                     editorcore.insertTag(this.tagname);
18137                     editor.focus();
18138                 }
18139             }
18140             
18141         });
18142        */
18143         
18144          
18145        this.xtype = 'NavSimplebar';
18146         
18147         for(var i=0;i< children.length;i++) {
18148             
18149             this.buttons.add(this.addxtypeChild(children[i]));
18150             
18151         }
18152         
18153         editor.on('editorevent', this.updateToolbar, this);
18154     },
18155     onBtnClick : function(id)
18156     {
18157        this.editorcore.relayCmd(id);
18158        this.editorcore.focus();
18159     },
18160     
18161     /**
18162      * Protected method that will not generally be called directly. It triggers
18163      * a toolbar update by reading the markup state of the current selection in the editor.
18164      */
18165     updateToolbar: function(){
18166
18167         if(!this.editorcore.activated){
18168             this.editor.onFirstFocus(); // is this neeed?
18169             return;
18170         }
18171
18172         var btns = this.buttons; 
18173         var doc = this.editorcore.doc;
18174         btns.get('bold').setActive(doc.queryCommandState('bold'));
18175         btns.get('italic').setActive(doc.queryCommandState('italic'));
18176         //btns.get('underline').setActive(doc.queryCommandState('underline'));
18177         
18178         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
18179         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
18180         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
18181         
18182         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
18183         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
18184          /*
18185         
18186         var ans = this.editorcore.getAllAncestors();
18187         if (this.formatCombo) {
18188             
18189             
18190             var store = this.formatCombo.store;
18191             this.formatCombo.setValue("");
18192             for (var i =0; i < ans.length;i++) {
18193                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
18194                     // select it..
18195                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
18196                     break;
18197                 }
18198             }
18199         }
18200         
18201         
18202         
18203         // hides menus... - so this cant be on a menu...
18204         Roo.bootstrap.MenuMgr.hideAll();
18205         */
18206         Roo.bootstrap.MenuMgr.hideAll();
18207         //this.editorsyncValue();
18208     },
18209     onFirstFocus: function() {
18210         this.buttons.each(function(item){
18211            item.enable();
18212         });
18213     },
18214     toggleSourceEdit : function(sourceEditMode){
18215         
18216           
18217         if(sourceEditMode){
18218             Roo.log("disabling buttons");
18219            this.buttons.each( function(item){
18220                 if(item.cmd != 'pencil'){
18221                     item.disable();
18222                 }
18223             });
18224           
18225         }else{
18226             Roo.log("enabling buttons");
18227             if(this.editorcore.initialized){
18228                 this.buttons.each( function(item){
18229                     item.enable();
18230                 });
18231             }
18232             
18233         }
18234         Roo.log("calling toggole on editor");
18235         // tell the editor that it's been pressed..
18236         this.editor.toggleSourceEdit(sourceEditMode);
18237        
18238     }
18239 });
18240
18241
18242
18243
18244
18245 /**
18246  * @class Roo.bootstrap.Table.AbstractSelectionModel
18247  * @extends Roo.util.Observable
18248  * Abstract base class for grid SelectionModels.  It provides the interface that should be
18249  * implemented by descendant classes.  This class should not be directly instantiated.
18250  * @constructor
18251  */
18252 Roo.bootstrap.Table.AbstractSelectionModel = function(){
18253     this.locked = false;
18254     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
18255 };
18256
18257
18258 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
18259     /** @ignore Called by the grid automatically. Do not call directly. */
18260     init : function(grid){
18261         this.grid = grid;
18262         this.initEvents();
18263     },
18264
18265     /**
18266      * Locks the selections.
18267      */
18268     lock : function(){
18269         this.locked = true;
18270     },
18271
18272     /**
18273      * Unlocks the selections.
18274      */
18275     unlock : function(){
18276         this.locked = false;
18277     },
18278
18279     /**
18280      * Returns true if the selections are locked.
18281      * @return {Boolean}
18282      */
18283     isLocked : function(){
18284         return this.locked;
18285     }
18286 });
18287 /**
18288  * @extends Roo.bootstrap.Table.AbstractSelectionModel
18289  * @class Roo.bootstrap.Table.RowSelectionModel
18290  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
18291  * It supports multiple selections and keyboard selection/navigation. 
18292  * @constructor
18293  * @param {Object} config
18294  */
18295
18296 Roo.bootstrap.Table.RowSelectionModel = function(config){
18297     Roo.apply(this, config);
18298     this.selections = new Roo.util.MixedCollection(false, function(o){
18299         return o.id;
18300     });
18301
18302     this.last = false;
18303     this.lastActive = false;
18304
18305     this.addEvents({
18306         /**
18307              * @event selectionchange
18308              * Fires when the selection changes
18309              * @param {SelectionModel} this
18310              */
18311             "selectionchange" : true,
18312         /**
18313              * @event afterselectionchange
18314              * Fires after the selection changes (eg. by key press or clicking)
18315              * @param {SelectionModel} this
18316              */
18317             "afterselectionchange" : true,
18318         /**
18319              * @event beforerowselect
18320              * Fires when a row is selected being selected, return false to cancel.
18321              * @param {SelectionModel} this
18322              * @param {Number} rowIndex The selected index
18323              * @param {Boolean} keepExisting False if other selections will be cleared
18324              */
18325             "beforerowselect" : true,
18326         /**
18327              * @event rowselect
18328              * Fires when a row is selected.
18329              * @param {SelectionModel} this
18330              * @param {Number} rowIndex The selected index
18331              * @param {Roo.data.Record} r The record
18332              */
18333             "rowselect" : true,
18334         /**
18335              * @event rowdeselect
18336              * Fires when a row is deselected.
18337              * @param {SelectionModel} this
18338              * @param {Number} rowIndex The selected index
18339              */
18340         "rowdeselect" : true
18341     });
18342     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
18343     this.locked = false;
18344 };
18345
18346 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
18347     /**
18348      * @cfg {Boolean} singleSelect
18349      * True to allow selection of only one row at a time (defaults to false)
18350      */
18351     singleSelect : false,
18352
18353     // private
18354     initEvents : function(){
18355
18356         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
18357             this.grid.on("mousedown", this.handleMouseDown, this);
18358         }else{ // allow click to work like normal
18359             this.grid.on("rowclick", this.handleDragableRowClick, this);
18360         }
18361
18362         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
18363             "up" : function(e){
18364                 if(!e.shiftKey){
18365                     this.selectPrevious(e.shiftKey);
18366                 }else if(this.last !== false && this.lastActive !== false){
18367                     var last = this.last;
18368                     this.selectRange(this.last,  this.lastActive-1);
18369                     this.grid.getView().focusRow(this.lastActive);
18370                     if(last !== false){
18371                         this.last = last;
18372                     }
18373                 }else{
18374                     this.selectFirstRow();
18375                 }
18376                 this.fireEvent("afterselectionchange", this);
18377             },
18378             "down" : function(e){
18379                 if(!e.shiftKey){
18380                     this.selectNext(e.shiftKey);
18381                 }else if(this.last !== false && this.lastActive !== false){
18382                     var last = this.last;
18383                     this.selectRange(this.last,  this.lastActive+1);
18384                     this.grid.getView().focusRow(this.lastActive);
18385                     if(last !== false){
18386                         this.last = last;
18387                     }
18388                 }else{
18389                     this.selectFirstRow();
18390                 }
18391                 this.fireEvent("afterselectionchange", this);
18392             },
18393             scope: this
18394         });
18395
18396         var view = this.grid.view;
18397         view.on("refresh", this.onRefresh, this);
18398         view.on("rowupdated", this.onRowUpdated, this);
18399         view.on("rowremoved", this.onRemove, this);
18400     },
18401
18402     // private
18403     onRefresh : function(){
18404         var ds = this.grid.dataSource, i, v = this.grid.view;
18405         var s = this.selections;
18406         s.each(function(r){
18407             if((i = ds.indexOfId(r.id)) != -1){
18408                 v.onRowSelect(i);
18409             }else{
18410                 s.remove(r);
18411             }
18412         });
18413     },
18414
18415     // private
18416     onRemove : function(v, index, r){
18417         this.selections.remove(r);
18418     },
18419
18420     // private
18421     onRowUpdated : function(v, index, r){
18422         if(this.isSelected(r)){
18423             v.onRowSelect(index);
18424         }
18425     },
18426
18427     /**
18428      * Select records.
18429      * @param {Array} records The records to select
18430      * @param {Boolean} keepExisting (optional) True to keep existing selections
18431      */
18432     selectRecords : function(records, keepExisting){
18433         if(!keepExisting){
18434             this.clearSelections();
18435         }
18436         var ds = this.grid.dataSource;
18437         for(var i = 0, len = records.length; i < len; i++){
18438             this.selectRow(ds.indexOf(records[i]), true);
18439         }
18440     },
18441
18442     /**
18443      * Gets the number of selected rows.
18444      * @return {Number}
18445      */
18446     getCount : function(){
18447         return this.selections.length;
18448     },
18449
18450     /**
18451      * Selects the first row in the grid.
18452      */
18453     selectFirstRow : function(){
18454         this.selectRow(0);
18455     },
18456
18457     /**
18458      * Select the last row.
18459      * @param {Boolean} keepExisting (optional) True to keep existing selections
18460      */
18461     selectLastRow : function(keepExisting){
18462         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
18463     },
18464
18465     /**
18466      * Selects the row immediately following the last selected row.
18467      * @param {Boolean} keepExisting (optional) True to keep existing selections
18468      */
18469     selectNext : function(keepExisting){
18470         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
18471             this.selectRow(this.last+1, keepExisting);
18472             this.grid.getView().focusRow(this.last);
18473         }
18474     },
18475
18476     /**
18477      * Selects the row that precedes the last selected row.
18478      * @param {Boolean} keepExisting (optional) True to keep existing selections
18479      */
18480     selectPrevious : function(keepExisting){
18481         if(this.last){
18482             this.selectRow(this.last-1, keepExisting);
18483             this.grid.getView().focusRow(this.last);
18484         }
18485     },
18486
18487     /**
18488      * Returns the selected records
18489      * @return {Array} Array of selected records
18490      */
18491     getSelections : function(){
18492         return [].concat(this.selections.items);
18493     },
18494
18495     /**
18496      * Returns the first selected record.
18497      * @return {Record}
18498      */
18499     getSelected : function(){
18500         return this.selections.itemAt(0);
18501     },
18502
18503
18504     /**
18505      * Clears all selections.
18506      */
18507     clearSelections : function(fast){
18508         if(this.locked) return;
18509         if(fast !== true){
18510             var ds = this.grid.dataSource;
18511             var s = this.selections;
18512             s.each(function(r){
18513                 this.deselectRow(ds.indexOfId(r.id));
18514             }, this);
18515             s.clear();
18516         }else{
18517             this.selections.clear();
18518         }
18519         this.last = false;
18520     },
18521
18522
18523     /**
18524      * Selects all rows.
18525      */
18526     selectAll : function(){
18527         if(this.locked) return;
18528         this.selections.clear();
18529         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
18530             this.selectRow(i, true);
18531         }
18532     },
18533
18534     /**
18535      * Returns True if there is a selection.
18536      * @return {Boolean}
18537      */
18538     hasSelection : function(){
18539         return this.selections.length > 0;
18540     },
18541
18542     /**
18543      * Returns True if the specified row is selected.
18544      * @param {Number/Record} record The record or index of the record to check
18545      * @return {Boolean}
18546      */
18547     isSelected : function(index){
18548         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
18549         return (r && this.selections.key(r.id) ? true : false);
18550     },
18551
18552     /**
18553      * Returns True if the specified record id is selected.
18554      * @param {String} id The id of record to check
18555      * @return {Boolean}
18556      */
18557     isIdSelected : function(id){
18558         return (this.selections.key(id) ? true : false);
18559     },
18560
18561     // private
18562     handleMouseDown : function(e, t){
18563         var view = this.grid.getView(), rowIndex;
18564         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
18565             return;
18566         };
18567         if(e.shiftKey && this.last !== false){
18568             var last = this.last;
18569             this.selectRange(last, rowIndex, e.ctrlKey);
18570             this.last = last; // reset the last
18571             view.focusRow(rowIndex);
18572         }else{
18573             var isSelected = this.isSelected(rowIndex);
18574             if(e.button !== 0 && isSelected){
18575                 view.focusRow(rowIndex);
18576             }else if(e.ctrlKey && isSelected){
18577                 this.deselectRow(rowIndex);
18578             }else if(!isSelected){
18579                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
18580                 view.focusRow(rowIndex);
18581             }
18582         }
18583         this.fireEvent("afterselectionchange", this);
18584     },
18585     // private
18586     handleDragableRowClick :  function(grid, rowIndex, e) 
18587     {
18588         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
18589             this.selectRow(rowIndex, false);
18590             grid.view.focusRow(rowIndex);
18591              this.fireEvent("afterselectionchange", this);
18592         }
18593     },
18594     
18595     /**
18596      * Selects multiple rows.
18597      * @param {Array} rows Array of the indexes of the row to select
18598      * @param {Boolean} keepExisting (optional) True to keep existing selections
18599      */
18600     selectRows : function(rows, keepExisting){
18601         if(!keepExisting){
18602             this.clearSelections();
18603         }
18604         for(var i = 0, len = rows.length; i < len; i++){
18605             this.selectRow(rows[i], true);
18606         }
18607     },
18608
18609     /**
18610      * Selects a range of rows. All rows in between startRow and endRow are also selected.
18611      * @param {Number} startRow The index of the first row in the range
18612      * @param {Number} endRow The index of the last row in the range
18613      * @param {Boolean} keepExisting (optional) True to retain existing selections
18614      */
18615     selectRange : function(startRow, endRow, keepExisting){
18616         if(this.locked) return;
18617         if(!keepExisting){
18618             this.clearSelections();
18619         }
18620         if(startRow <= endRow){
18621             for(var i = startRow; i <= endRow; i++){
18622                 this.selectRow(i, true);
18623             }
18624         }else{
18625             for(var i = startRow; i >= endRow; i--){
18626                 this.selectRow(i, true);
18627             }
18628         }
18629     },
18630
18631     /**
18632      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
18633      * @param {Number} startRow The index of the first row in the range
18634      * @param {Number} endRow The index of the last row in the range
18635      */
18636     deselectRange : function(startRow, endRow, preventViewNotify){
18637         if(this.locked) return;
18638         for(var i = startRow; i <= endRow; i++){
18639             this.deselectRow(i, preventViewNotify);
18640         }
18641     },
18642
18643     /**
18644      * Selects a row.
18645      * @param {Number} row The index of the row to select
18646      * @param {Boolean} keepExisting (optional) True to keep existing selections
18647      */
18648     selectRow : function(index, keepExisting, preventViewNotify){
18649         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
18650         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
18651             if(!keepExisting || this.singleSelect){
18652                 this.clearSelections();
18653             }
18654             var r = this.grid.dataSource.getAt(index);
18655             this.selections.add(r);
18656             this.last = this.lastActive = index;
18657             if(!preventViewNotify){
18658                 this.grid.getView().onRowSelect(index);
18659             }
18660             this.fireEvent("rowselect", this, index, r);
18661             this.fireEvent("selectionchange", this);
18662         }
18663     },
18664
18665     /**
18666      * Deselects a row.
18667      * @param {Number} row The index of the row to deselect
18668      */
18669     deselectRow : function(index, preventViewNotify){
18670         if(this.locked) return;
18671         if(this.last == index){
18672             this.last = false;
18673         }
18674         if(this.lastActive == index){
18675             this.lastActive = false;
18676         }
18677         var r = this.grid.dataSource.getAt(index);
18678         this.selections.remove(r);
18679         if(!preventViewNotify){
18680             this.grid.getView().onRowDeselect(index);
18681         }
18682         this.fireEvent("rowdeselect", this, index);
18683         this.fireEvent("selectionchange", this);
18684     },
18685
18686     // private
18687     restoreLast : function(){
18688         if(this._last){
18689             this.last = this._last;
18690         }
18691     },
18692
18693     // private
18694     acceptsNav : function(row, col, cm){
18695         return !cm.isHidden(col) && cm.isCellEditable(col, row);
18696     },
18697
18698     // private
18699     onEditorKey : function(field, e){
18700         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
18701         if(k == e.TAB){
18702             e.stopEvent();
18703             ed.completeEdit();
18704             if(e.shiftKey){
18705                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
18706             }else{
18707                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
18708             }
18709         }else if(k == e.ENTER && !e.ctrlKey){
18710             e.stopEvent();
18711             ed.completeEdit();
18712             if(e.shiftKey){
18713                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
18714             }else{
18715                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
18716             }
18717         }else if(k == e.ESC){
18718             ed.cancelEdit();
18719         }
18720         if(newCell){
18721             g.startEditing(newCell[0], newCell[1]);
18722         }
18723     }
18724 });/*
18725  * Based on:
18726  * Ext JS Library 1.1.1
18727  * Copyright(c) 2006-2007, Ext JS, LLC.
18728  *
18729  * Originally Released Under LGPL - original licence link has changed is not relivant.
18730  *
18731  * Fork - LGPL
18732  * <script type="text/javascript">
18733  */
18734  
18735 /**
18736  * @class Roo.bootstrap.PagingToolbar
18737  * @extends Roo.Row
18738  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
18739  * @constructor
18740  * Create a new PagingToolbar
18741  * @param {Object} config The config object
18742  */
18743 Roo.bootstrap.PagingToolbar = function(config)
18744 {
18745     // old args format still supported... - xtype is prefered..
18746         // created from xtype...
18747     var ds = config.dataSource;
18748     this.toolbarItems = [];
18749     if (config.items) {
18750         this.toolbarItems = config.items;
18751 //        config.items = [];
18752     }
18753     
18754     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
18755     this.ds = ds;
18756     this.cursor = 0;
18757     if (ds) { 
18758         this.bind(ds);
18759     }
18760     
18761     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
18762     
18763 };
18764
18765 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
18766     /**
18767      * @cfg {Roo.data.Store} dataSource
18768      * The underlying data store providing the paged data
18769      */
18770     /**
18771      * @cfg {String/HTMLElement/Element} container
18772      * container The id or element that will contain the toolbar
18773      */
18774     /**
18775      * @cfg {Boolean} displayInfo
18776      * True to display the displayMsg (defaults to false)
18777      */
18778     /**
18779      * @cfg {Number} pageSize
18780      * The number of records to display per page (defaults to 20)
18781      */
18782     pageSize: 20,
18783     /**
18784      * @cfg {String} displayMsg
18785      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
18786      */
18787     displayMsg : 'Displaying {0} - {1} of {2}',
18788     /**
18789      * @cfg {String} emptyMsg
18790      * The message to display when no records are found (defaults to "No data to display")
18791      */
18792     emptyMsg : 'No data to display',
18793     /**
18794      * Customizable piece of the default paging text (defaults to "Page")
18795      * @type String
18796      */
18797     beforePageText : "Page",
18798     /**
18799      * Customizable piece of the default paging text (defaults to "of %0")
18800      * @type String
18801      */
18802     afterPageText : "of {0}",
18803     /**
18804      * Customizable piece of the default paging text (defaults to "First Page")
18805      * @type String
18806      */
18807     firstText : "First Page",
18808     /**
18809      * Customizable piece of the default paging text (defaults to "Previous Page")
18810      * @type String
18811      */
18812     prevText : "Previous Page",
18813     /**
18814      * Customizable piece of the default paging text (defaults to "Next Page")
18815      * @type String
18816      */
18817     nextText : "Next Page",
18818     /**
18819      * Customizable piece of the default paging text (defaults to "Last Page")
18820      * @type String
18821      */
18822     lastText : "Last Page",
18823     /**
18824      * Customizable piece of the default paging text (defaults to "Refresh")
18825      * @type String
18826      */
18827     refreshText : "Refresh",
18828
18829     buttons : false,
18830     // private
18831     onRender : function(ct, position) 
18832     {
18833         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
18834         this.navgroup.parentId = this.id;
18835         this.navgroup.onRender(this.el, null);
18836         // add the buttons to the navgroup
18837         
18838         if(this.displayInfo){
18839             Roo.log(this.el.select('ul.navbar-nav',true).first());
18840             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
18841             this.displayEl = this.el.select('.x-paging-info', true).first();
18842 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
18843 //            this.displayEl = navel.el.select('span',true).first();
18844         }
18845         
18846         var _this = this;
18847         
18848         if(this.buttons){
18849             Roo.each(_this.buttons, function(e){
18850                Roo.factory(e).onRender(_this.el, null);
18851             });
18852         }
18853             
18854         Roo.each(_this.toolbarItems, function(e) {
18855             _this.navgroup.addItem(e);
18856         });
18857         
18858         this.first = this.navgroup.addItem({
18859             tooltip: this.firstText,
18860             cls: "prev",
18861             icon : 'fa fa-backward',
18862             disabled: true,
18863             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
18864         });
18865         
18866         this.prev =  this.navgroup.addItem({
18867             tooltip: this.prevText,
18868             cls: "prev",
18869             icon : 'fa fa-step-backward',
18870             disabled: true,
18871             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
18872         });
18873     //this.addSeparator();
18874         
18875         
18876         var field = this.navgroup.addItem( {
18877             tagtype : 'span',
18878             cls : 'x-paging-position',
18879             
18880             html : this.beforePageText  +
18881                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
18882                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
18883          } ); //?? escaped?
18884         
18885         this.field = field.el.select('input', true).first();
18886         this.field.on("keydown", this.onPagingKeydown, this);
18887         this.field.on("focus", function(){this.dom.select();});
18888     
18889     
18890         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
18891         //this.field.setHeight(18);
18892         //this.addSeparator();
18893         this.next = this.navgroup.addItem({
18894             tooltip: this.nextText,
18895             cls: "next",
18896             html : ' <i class="fa fa-step-forward">',
18897             disabled: true,
18898             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
18899         });
18900         this.last = this.navgroup.addItem({
18901             tooltip: this.lastText,
18902             icon : 'fa fa-forward',
18903             cls: "next",
18904             disabled: true,
18905             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
18906         });
18907     //this.addSeparator();
18908         this.loading = this.navgroup.addItem({
18909             tooltip: this.refreshText,
18910             icon: 'fa fa-refresh',
18911             
18912             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
18913         });
18914
18915     },
18916
18917     // private
18918     updateInfo : function(){
18919         if(this.displayEl){
18920             var count = this.ds.getCount();
18921             var msg = count == 0 ?
18922                 this.emptyMsg :
18923                 String.format(
18924                     this.displayMsg,
18925                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
18926                 );
18927             this.displayEl.update(msg);
18928         }
18929     },
18930
18931     // private
18932     onLoad : function(ds, r, o){
18933        this.cursor = o.params ? o.params.start : 0;
18934        var d = this.getPageData(),
18935             ap = d.activePage,
18936             ps = d.pages;
18937         
18938        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
18939        this.field.dom.value = ap;
18940        this.first.setDisabled(ap == 1);
18941        this.prev.setDisabled(ap == 1);
18942        this.next.setDisabled(ap == ps);
18943        this.last.setDisabled(ap == ps);
18944        this.loading.enable();
18945        this.updateInfo();
18946     },
18947
18948     // private
18949     getPageData : function(){
18950         var total = this.ds.getTotalCount();
18951         return {
18952             total : total,
18953             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
18954             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
18955         };
18956     },
18957
18958     // private
18959     onLoadError : function(){
18960         this.loading.enable();
18961     },
18962
18963     // private
18964     onPagingKeydown : function(e){
18965         var k = e.getKey();
18966         var d = this.getPageData();
18967         if(k == e.RETURN){
18968             var v = this.field.dom.value, pageNum;
18969             if(!v || isNaN(pageNum = parseInt(v, 10))){
18970                 this.field.dom.value = d.activePage;
18971                 return;
18972             }
18973             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
18974             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
18975             e.stopEvent();
18976         }
18977         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))
18978         {
18979           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
18980           this.field.dom.value = pageNum;
18981           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
18982           e.stopEvent();
18983         }
18984         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
18985         {
18986           var v = this.field.dom.value, pageNum; 
18987           var increment = (e.shiftKey) ? 10 : 1;
18988           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
18989             increment *= -1;
18990           if(!v || isNaN(pageNum = parseInt(v, 10))) {
18991             this.field.dom.value = d.activePage;
18992             return;
18993           }
18994           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
18995           {
18996             this.field.dom.value = parseInt(v, 10) + increment;
18997             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
18998             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
18999           }
19000           e.stopEvent();
19001         }
19002     },
19003
19004     // private
19005     beforeLoad : function(){
19006         if(this.loading){
19007             this.loading.disable();
19008         }
19009     },
19010
19011     // private
19012     onClick : function(which){
19013         var ds = this.ds;
19014         if (!ds) {
19015             return;
19016         }
19017         switch(which){
19018             case "first":
19019                 ds.load({params:{start: 0, limit: this.pageSize}});
19020             break;
19021             case "prev":
19022                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
19023             break;
19024             case "next":
19025                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
19026             break;
19027             case "last":
19028                 var total = ds.getTotalCount();
19029                 var extra = total % this.pageSize;
19030                 var lastStart = extra ? (total - extra) : total-this.pageSize;
19031                 ds.load({params:{start: lastStart, limit: this.pageSize}});
19032             break;
19033             case "refresh":
19034                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
19035             break;
19036         }
19037     },
19038
19039     /**
19040      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
19041      * @param {Roo.data.Store} store The data store to unbind
19042      */
19043     unbind : function(ds){
19044         ds.un("beforeload", this.beforeLoad, this);
19045         ds.un("load", this.onLoad, this);
19046         ds.un("loadexception", this.onLoadError, this);
19047         ds.un("remove", this.updateInfo, this);
19048         ds.un("add", this.updateInfo, this);
19049         this.ds = undefined;
19050     },
19051
19052     /**
19053      * Binds the paging toolbar to the specified {@link Roo.data.Store}
19054      * @param {Roo.data.Store} store The data store to bind
19055      */
19056     bind : function(ds){
19057         ds.on("beforeload", this.beforeLoad, this);
19058         ds.on("load", this.onLoad, this);
19059         ds.on("loadexception", this.onLoadError, this);
19060         ds.on("remove", this.updateInfo, this);
19061         ds.on("add", this.updateInfo, this);
19062         this.ds = ds;
19063     }
19064 });/*
19065  * - LGPL
19066  *
19067  * element
19068  * 
19069  */
19070
19071 /**
19072  * @class Roo.bootstrap.MessageBar
19073  * @extends Roo.bootstrap.Component
19074  * Bootstrap MessageBar class
19075  * @cfg {String} html contents of the MessageBar
19076  * @cfg {String} weight (info | success | warning | danger) default info
19077  * @cfg {String} beforeClass insert the bar before the given class
19078  * @cfg {Boolean} closable (true | false) default false
19079  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
19080  * 
19081  * @constructor
19082  * Create a new Element
19083  * @param {Object} config The config object
19084  */
19085
19086 Roo.bootstrap.MessageBar = function(config){
19087     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
19088 };
19089
19090 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
19091     
19092     html: '',
19093     weight: 'info',
19094     closable: false,
19095     fixed: false,
19096     beforeClass: 'bootstrap-sticky-wrap',
19097     
19098     getAutoCreate : function(){
19099         
19100         var cfg = {
19101             tag: 'div',
19102             cls: 'alert alert-dismissable alert-' + this.weight,
19103             cn: [
19104                 {
19105                     tag: 'span',
19106                     cls: 'message',
19107                     html: this.html || ''
19108                 }
19109             ]
19110         }
19111         
19112         if(this.fixed){
19113             cfg.cls += ' alert-messages-fixed';
19114         }
19115         
19116         if(this.closable){
19117             cfg.cn.push({
19118                 tag: 'button',
19119                 cls: 'close',
19120                 html: 'x'
19121             });
19122         }
19123         
19124         return cfg;
19125     },
19126     
19127     onRender : function(ct, position)
19128     {
19129         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19130         
19131         if(!this.el){
19132             var cfg = Roo.apply({},  this.getAutoCreate());
19133             cfg.id = Roo.id();
19134             
19135             if (this.cls) {
19136                 cfg.cls += ' ' + this.cls;
19137             }
19138             if (this.style) {
19139                 cfg.style = this.style;
19140             }
19141             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
19142             
19143             this.el.setVisibilityMode(Roo.Element.DISPLAY);
19144         }
19145         
19146         this.el.select('>button.close').on('click', this.hide, this);
19147         
19148     },
19149     
19150     show : function()
19151     {
19152         if (!this.rendered) {
19153             this.render();
19154         }
19155         
19156         this.el.show();
19157         
19158         this.fireEvent('show', this);
19159         
19160     },
19161     
19162     hide : function()
19163     {
19164         if (!this.rendered) {
19165             this.render();
19166         }
19167         
19168         this.el.hide();
19169         
19170         this.fireEvent('hide', this);
19171     },
19172     
19173     update : function()
19174     {
19175 //        var e = this.el.dom.firstChild;
19176 //        
19177 //        if(this.closable){
19178 //            e = e.nextSibling;
19179 //        }
19180 //        
19181 //        e.data = this.html || '';
19182
19183         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
19184     }
19185    
19186 });
19187
19188  
19189
19190      /*
19191  * - LGPL
19192  *
19193  * Graph
19194  * 
19195  */
19196
19197
19198 /**
19199  * @class Roo.bootstrap.Graph
19200  * @extends Roo.bootstrap.Component
19201  * Bootstrap Graph class
19202 > Prameters
19203  -sm {number} sm 4
19204  -md {number} md 5
19205  @cfg {String} graphtype  bar | vbar | pie
19206  @cfg {number} g_x coodinator | centre x (pie)
19207  @cfg {number} g_y coodinator | centre y (pie)
19208  @cfg {number} g_r radius (pie)
19209  @cfg {number} g_height height of the chart (respected by all elements in the set)
19210  @cfg {number} g_width width of the chart (respected by all elements in the set)
19211  @cfg {Object} title The title of the chart
19212     
19213  -{Array}  values
19214  -opts (object) options for the chart 
19215      o {
19216      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
19217      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
19218      o vgutter (number)
19219      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.
19220      o stacked (boolean) whether or not to tread values as in a stacked bar chart
19221      o to
19222      o stretch (boolean)
19223      o }
19224  -opts (object) options for the pie
19225      o{
19226      o cut
19227      o startAngle (number)
19228      o endAngle (number)
19229      } 
19230  *
19231  * @constructor
19232  * Create a new Input
19233  * @param {Object} config The config object
19234  */
19235
19236 Roo.bootstrap.Graph = function(config){
19237     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
19238     
19239     this.addEvents({
19240         // img events
19241         /**
19242          * @event click
19243          * The img click event for the img.
19244          * @param {Roo.EventObject} e
19245          */
19246         "click" : true
19247     });
19248 };
19249
19250 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
19251     
19252     sm: 4,
19253     md: 5,
19254     graphtype: 'bar',
19255     g_height: 250,
19256     g_width: 400,
19257     g_x: 50,
19258     g_y: 50,
19259     g_r: 30,
19260     opts:{
19261         //g_colors: this.colors,
19262         g_type: 'soft',
19263         g_gutter: '20%'
19264
19265     },
19266     title : false,
19267
19268     getAutoCreate : function(){
19269         
19270         var cfg = {
19271             tag: 'div',
19272             html : null
19273         }
19274         
19275         
19276         return  cfg;
19277     },
19278
19279     onRender : function(ct,position){
19280         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
19281         this.raphael = Raphael(this.el.dom);
19282         
19283                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19284                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19285                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19286                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
19287                 /*
19288                 r.text(160, 10, "Single Series Chart").attr(txtattr);
19289                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
19290                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
19291                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
19292                 
19293                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
19294                 r.barchart(330, 10, 300, 220, data1);
19295                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
19296                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
19297                 */
19298                 
19299                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19300                 // r.barchart(30, 30, 560, 250,  xdata, {
19301                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
19302                 //     axis : "0 0 1 1",
19303                 //     axisxlabels :  xdata
19304                 //     //yvalues : cols,
19305                    
19306                 // });
19307 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19308 //        
19309 //        this.load(null,xdata,{
19310 //                axis : "0 0 1 1",
19311 //                axisxlabels :  xdata
19312 //                });
19313
19314     },
19315
19316     load : function(graphtype,xdata,opts){
19317         this.raphael.clear();
19318         if(!graphtype) {
19319             graphtype = this.graphtype;
19320         }
19321         if(!opts){
19322             opts = this.opts;
19323         }
19324         var r = this.raphael,
19325             fin = function () {
19326                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
19327             },
19328             fout = function () {
19329                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
19330             },
19331             pfin = function() {
19332                 this.sector.stop();
19333                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
19334
19335                 if (this.label) {
19336                     this.label[0].stop();
19337                     this.label[0].attr({ r: 7.5 });
19338                     this.label[1].attr({ "font-weight": 800 });
19339                 }
19340             },
19341             pfout = function() {
19342                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
19343
19344                 if (this.label) {
19345                     this.label[0].animate({ r: 5 }, 500, "bounce");
19346                     this.label[1].attr({ "font-weight": 400 });
19347                 }
19348             };
19349
19350         switch(graphtype){
19351             case 'bar':
19352                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19353                 break;
19354             case 'hbar':
19355                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19356                 break;
19357             case 'pie':
19358 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
19359 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
19360 //            
19361                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
19362                 
19363                 break;
19364
19365         }
19366         
19367         if(this.title){
19368             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
19369         }
19370         
19371     },
19372     
19373     setTitle: function(o)
19374     {
19375         this.title = o;
19376     },
19377     
19378     initEvents: function() {
19379         
19380         if(!this.href){
19381             this.el.on('click', this.onClick, this);
19382         }
19383     },
19384     
19385     onClick : function(e)
19386     {
19387         Roo.log('img onclick');
19388         this.fireEvent('click', this, e);
19389     }
19390    
19391 });
19392
19393  
19394 /*
19395  * - LGPL
19396  *
19397  * numberBox
19398  * 
19399  */
19400 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19401
19402 /**
19403  * @class Roo.bootstrap.dash.NumberBox
19404  * @extends Roo.bootstrap.Component
19405  * Bootstrap NumberBox class
19406  * @cfg {String} bgcolor Box background color, such as (aqua | green | yellow | red etc..) Default aqua
19407  * @cfg {String} headline Box headline
19408  * @cfg {String} content Box content
19409  * @cfg {String} icon Box icon
19410  * @cfg {String} footer Footer text
19411  * @cfg {String} fhref Footer href
19412  * 
19413  * @constructor
19414  * Create a new NumberBox
19415  * @param {Object} config The config object
19416  */
19417
19418
19419 Roo.bootstrap.dash.NumberBox = function(config){
19420     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
19421     
19422 };
19423
19424 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
19425     
19426     bgcolor : 'aqua',
19427     headline : '',
19428     content : '',
19429     icon : '',
19430     footer : '',
19431     fhref : '',
19432     ficon : '',
19433     
19434     getAutoCreate : function(){
19435         
19436         var cfg = {
19437             tag : 'div',
19438             cls : 'small-box bg-' + this.bgcolor,
19439             cn : [
19440                 {
19441                     tag : 'div',
19442                     cls : 'inner',
19443                     cn :[
19444                         {
19445                             tag : 'h3',
19446                             cls : 'roo-headline',
19447                             html : this.headline
19448                         },
19449                         {
19450                             tag : 'p',
19451                             cls : 'roo-content',
19452                             html : this.content
19453                         }
19454                     ]
19455                 }
19456             ]
19457         }
19458         
19459         if(this.icon){
19460             cfg.cn.push({
19461                 tag : 'div',
19462                 cls : 'icon',
19463                 cn :[
19464                     {
19465                         tag : 'i',
19466                         cls : 'ion ' + this.icon
19467                     }
19468                 ]
19469             });
19470         }
19471         
19472         if(this.footer){
19473             var footer = {
19474                 tag : 'a',
19475                 cls : 'small-box-footer',
19476                 href : this.fhref || '#',
19477                 html : this.footer
19478             };
19479             
19480             cfg.cn.push(footer);
19481             
19482         }
19483         
19484         return  cfg;
19485     },
19486
19487     onRender : function(ct,position){
19488         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
19489
19490
19491        
19492                 
19493     },
19494
19495     setHeadline: function (value)
19496     {
19497         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
19498     },
19499     
19500     setFooter: function (value, href)
19501     {
19502         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
19503         
19504         if(href){
19505             this.el.select('a.small-box-footer',true).first().attr('href', href);
19506         }
19507         
19508     },
19509
19510     setContent: function (value)
19511     {
19512         this.el.select('.roo-content',true).first().dom.innerHTML = value;
19513     },
19514
19515     initEvents: function() 
19516     {   
19517         
19518     }
19519     
19520 });
19521
19522  
19523 /*
19524  * - LGPL
19525  *
19526  * TabBox
19527  * 
19528  */
19529 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19530
19531 /**
19532  * @class Roo.bootstrap.dash.TabBox
19533  * @extends Roo.bootstrap.Component
19534  * Bootstrap TabBox class
19535  * @cfg {String} title Title of the TabBox
19536  * @cfg {String} icon Icon of the TabBox
19537  * 
19538  * @constructor
19539  * Create a new TabBox
19540  * @param {Object} config The config object
19541  */
19542
19543
19544 Roo.bootstrap.dash.TabBox = function(config){
19545     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
19546     this.addEvents({
19547         // raw events
19548         /**
19549          * @event addpane
19550          * When a pane is added
19551          * @param {Roo.bootstrap.dash.TabPane} pane
19552          */
19553         "addpane" : true
19554          
19555     });
19556 };
19557
19558 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
19559
19560     title : '',
19561     icon : false,
19562     
19563     getChildContainer : function()
19564     {
19565         return this.el.select('.tab-content', true).first();
19566     },
19567     
19568     getAutoCreate : function(){
19569         
19570         var header = {
19571             tag: 'li',
19572             cls: 'pull-left header',
19573             html: this.title,
19574             cn : []
19575         };
19576         
19577         if(this.icon){
19578             header.cn.push({
19579                 tag: 'i',
19580                 cls: 'fa ' + this.icon
19581             });
19582         }
19583         
19584         
19585         var cfg = {
19586             tag: 'div',
19587             cls: 'nav-tabs-custom',
19588             cn: [
19589                 {
19590                     tag: 'ul',
19591                     cls: 'nav nav-tabs pull-right',
19592                     cn: [
19593                         header
19594                     ]
19595                 },
19596                 {
19597                     tag: 'div',
19598                     cls: 'tab-content no-padding',
19599                     cn: []
19600                 }
19601             ]
19602         }
19603
19604         return  cfg;
19605     },
19606     initEvents : function()
19607     {
19608         //Roo.log('add add pane handler');
19609         this.on('addpane', this.onAddPane, this);
19610     },
19611      /**
19612      * Updates the box title
19613      * @param {String} html to set the title to.
19614      */
19615     setTitle : function(value)
19616     {
19617         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
19618     },
19619     onAddPane : function(pane)
19620     {
19621         //Roo.log('addpane');
19622         //Roo.log(pane);
19623         // tabs are rendere left to right..
19624         var ctr = this.el.select('.nav-tabs', true).first();
19625          
19626          
19627         var existing = ctr.select('.nav-tab',true);
19628         var qty = existing.getCount();;
19629         
19630         
19631         var tab = ctr.createChild({
19632             tag : 'li',
19633             cls : 'nav-tab' + (qty ? '' : ' active'),
19634             cn : [
19635                 {
19636                     tag : 'a',
19637                     href:'#',
19638                     html : pane.title
19639                 }
19640             ]
19641         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
19642         pane.tab = tab;
19643         
19644         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
19645         if (!qty) {
19646             pane.el.addClass('active');
19647         }
19648         
19649                 
19650     },
19651     onTabClick : function(ev,un,ob,pane)
19652     {
19653         //Roo.log('tab - prev default');
19654         ev.preventDefault();
19655         
19656         
19657         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
19658         pane.tab.addClass('active');
19659         //Roo.log(pane.title);
19660         this.getChildContainer().select('.tab-pane',true).removeClass('active');
19661         // technically we should have a deactivate event.. but maybe add later.
19662         // and it should not de-activate the selected tab...
19663         
19664         pane.el.addClass('active');
19665         pane.fireEvent('activate');
19666         
19667         
19668     }
19669     
19670     
19671 });
19672
19673  
19674 /*
19675  * - LGPL
19676  *
19677  * Tab pane
19678  * 
19679  */
19680 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19681 /**
19682  * @class Roo.bootstrap.TabPane
19683  * @extends Roo.bootstrap.Component
19684  * Bootstrap TabPane class
19685  * @cfg {Boolean} active (false | true) Default false
19686  * @cfg {String} title title of panel
19687
19688  * 
19689  * @constructor
19690  * Create a new TabPane
19691  * @param {Object} config The config object
19692  */
19693
19694 Roo.bootstrap.dash.TabPane = function(config){
19695     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
19696     
19697 };
19698
19699 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
19700     
19701     active : false,
19702     title : '',
19703     
19704     // the tabBox that this is attached to.
19705     tab : false,
19706      
19707     getAutoCreate : function() 
19708     {
19709         var cfg = {
19710             tag: 'div',
19711             cls: 'tab-pane'
19712         }
19713         
19714         if(this.active){
19715             cfg.cls += ' active';
19716         }
19717         
19718         return cfg;
19719     },
19720     initEvents  : function()
19721     {
19722         //Roo.log('trigger add pane handler');
19723         this.parent().fireEvent('addpane', this)
19724     },
19725     
19726      /**
19727      * Updates the tab title 
19728      * @param {String} html to set the title to.
19729      */
19730     setTitle: function(str)
19731     {
19732         if (!this.tab) {
19733             return;
19734         }
19735         this.title = str;
19736         this.tab.select('a'.true).first().dom.innerHTML = str;
19737         
19738     }
19739     
19740     
19741     
19742 });
19743
19744  
19745
19746
19747  /*
19748  * - LGPL
19749  *
19750  * menu
19751  * 
19752  */
19753 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
19754
19755 /**
19756  * @class Roo.bootstrap.menu.Menu
19757  * @extends Roo.bootstrap.Component
19758  * Bootstrap Menu class - container for Menu
19759  * @cfg {String} html Text of the menu
19760  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
19761  * @cfg {String} icon Font awesome icon
19762  * @cfg {String} pos Menu align to (top | bottom) default bottom
19763  * 
19764  * 
19765  * @constructor
19766  * Create a new Menu
19767  * @param {Object} config The config object
19768  */
19769
19770
19771 Roo.bootstrap.menu.Menu = function(config){
19772     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
19773     
19774     this.addEvents({
19775         /**
19776          * @event beforeshow
19777          * Fires before this menu is displayed
19778          * @param {Roo.bootstrap.menu.Menu} this
19779          */
19780         beforeshow : true,
19781         /**
19782          * @event beforehide
19783          * Fires before this menu is hidden
19784          * @param {Roo.bootstrap.menu.Menu} this
19785          */
19786         beforehide : true,
19787         /**
19788          * @event show
19789          * Fires after this menu is displayed
19790          * @param {Roo.bootstrap.menu.Menu} this
19791          */
19792         show : true,
19793         /**
19794          * @event hide
19795          * Fires after this menu is hidden
19796          * @param {Roo.bootstrap.menu.Menu} this
19797          */
19798         hide : true,
19799         /**
19800          * @event click
19801          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19802          * @param {Roo.bootstrap.menu.Menu} this
19803          * @param {Roo.EventObject} e
19804          */
19805         click : true
19806     });
19807     
19808 };
19809
19810 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
19811     
19812     submenu : false,
19813     html : '',
19814     weight : 'default',
19815     icon : false,
19816     pos : 'bottom',
19817     
19818     
19819     getChildContainer : function() {
19820         if(this.isSubMenu){
19821             return this.el;
19822         }
19823         
19824         return this.el.select('ul.dropdown-menu', true).first();  
19825     },
19826     
19827     getAutoCreate : function()
19828     {
19829         var text = [
19830             {
19831                 tag : 'span',
19832                 cls : 'roo-menu-text',
19833                 html : this.html
19834             }
19835         ];
19836         
19837         if(this.icon){
19838             text.unshift({
19839                 tag : 'i',
19840                 cls : 'fa ' + this.icon
19841             })
19842         }
19843         
19844         
19845         var cfg = {
19846             tag : 'div',
19847             cls : 'btn-group',
19848             cn : [
19849                 {
19850                     tag : 'button',
19851                     cls : 'dropdown-button btn btn-' + this.weight,
19852                     cn : text
19853                 },
19854                 {
19855                     tag : 'button',
19856                     cls : 'dropdown-toggle btn btn-' + this.weight,
19857                     cn : [
19858                         {
19859                             tag : 'span',
19860                             cls : 'caret'
19861                         }
19862                     ]
19863                 },
19864                 {
19865                     tag : 'ul',
19866                     cls : 'dropdown-menu'
19867                 }
19868             ]
19869             
19870         };
19871         
19872         if(this.pos == 'top'){
19873             cfg.cls += ' dropup';
19874         }
19875         
19876         if(this.isSubMenu){
19877             cfg = {
19878                 tag : 'ul',
19879                 cls : 'dropdown-menu'
19880             }
19881         }
19882         
19883         return cfg;
19884     },
19885     
19886     onRender : function(ct, position)
19887     {
19888         this.isSubMenu = ct.hasClass('dropdown-submenu');
19889         
19890         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
19891     },
19892     
19893     initEvents : function() 
19894     {
19895         if(this.isSubMenu){
19896             return;
19897         }
19898         
19899         this.hidden = true;
19900         
19901         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
19902         this.triggerEl.on('click', this.onTriggerPress, this);
19903         
19904         this.buttonEl = this.el.select('button.dropdown-button', true).first();
19905         this.buttonEl.on('click', this.onClick, this);
19906         
19907     },
19908     
19909     list : function()
19910     {
19911         if(this.isSubMenu){
19912             return this.el;
19913         }
19914         
19915         return this.el.select('ul.dropdown-menu', true).first();
19916     },
19917     
19918     onClick : function(e)
19919     {
19920         this.fireEvent("click", this, e);
19921     },
19922     
19923     onTriggerPress  : function(e)
19924     {   
19925         if (this.isVisible()) {
19926             this.hide();
19927         } else {
19928             this.show();
19929         }
19930     },
19931     
19932     isVisible : function(){
19933         return !this.hidden;
19934     },
19935     
19936     show : function()
19937     {
19938         this.fireEvent("beforeshow", this);
19939         
19940         this.hidden = false;
19941         this.el.addClass('open');
19942         
19943         Roo.get(document).on("mouseup", this.onMouseUp, this);
19944         
19945         this.fireEvent("show", this);
19946         
19947         
19948     },
19949     
19950     hide : function()
19951     {
19952         this.fireEvent("beforehide", this);
19953         
19954         this.hidden = true;
19955         this.el.removeClass('open');
19956         
19957         Roo.get(document).un("mouseup", this.onMouseUp);
19958         
19959         this.fireEvent("hide", this);
19960     },
19961     
19962     onMouseUp : function()
19963     {
19964         this.hide();
19965     }
19966     
19967 });
19968
19969  
19970  /*
19971  * - LGPL
19972  *
19973  * menu item
19974  * 
19975  */
19976 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
19977
19978 /**
19979  * @class Roo.bootstrap.menu.Item
19980  * @extends Roo.bootstrap.Component
19981  * Bootstrap MenuItem class
19982  * @cfg {Boolean} submenu (true | false) default false
19983  * @cfg {String} html text of the item
19984  * @cfg {String} href the link
19985  * @cfg {Boolean} disable (true | false) default false
19986  * @cfg {Boolean} preventDefault (true | false) default true
19987  * @cfg {String} icon Font awesome icon
19988  * @cfg {String} pos Submenu align to (left | right) default right 
19989  * 
19990  * 
19991  * @constructor
19992  * Create a new Item
19993  * @param {Object} config The config object
19994  */
19995
19996
19997 Roo.bootstrap.menu.Item = function(config){
19998     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
19999     this.addEvents({
20000         /**
20001          * @event mouseover
20002          * Fires when the mouse is hovering over this menu
20003          * @param {Roo.bootstrap.menu.Item} this
20004          * @param {Roo.EventObject} e
20005          */
20006         mouseover : true,
20007         /**
20008          * @event mouseout
20009          * Fires when the mouse exits this menu
20010          * @param {Roo.bootstrap.menu.Item} this
20011          * @param {Roo.EventObject} e
20012          */
20013         mouseout : true,
20014         // raw events
20015         /**
20016          * @event click
20017          * The raw click event for the entire grid.
20018          * @param {Roo.EventObject} e
20019          */
20020         click : true
20021     });
20022 };
20023
20024 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
20025     
20026     submenu : false,
20027     href : '',
20028     html : '',
20029     preventDefault: true,
20030     disable : false,
20031     icon : false,
20032     pos : 'right',
20033     
20034     getAutoCreate : function()
20035     {
20036         var text = [
20037             {
20038                 tag : 'span',
20039                 cls : 'roo-menu-item-text',
20040                 html : this.html
20041             }
20042         ];
20043         
20044         if(this.icon){
20045             text.unshift({
20046                 tag : 'i',
20047                 cls : 'fa ' + this.icon
20048             })
20049         }
20050         
20051         var cfg = {
20052             tag : 'li',
20053             cn : [
20054                 {
20055                     tag : 'a',
20056                     href : this.href || '#',
20057                     cn : text
20058                 }
20059             ]
20060         };
20061         
20062         if(this.disable){
20063             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
20064         }
20065         
20066         if(this.submenu){
20067             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
20068             
20069             if(this.pos == 'left'){
20070                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
20071             }
20072         }
20073         
20074         return cfg;
20075     },
20076     
20077     initEvents : function() 
20078     {
20079         this.el.on('mouseover', this.onMouseOver, this);
20080         this.el.on('mouseout', this.onMouseOut, this);
20081         
20082         this.el.select('a', true).first().on('click', this.onClick, this);
20083         
20084     },
20085     
20086     onClick : function(e)
20087     {
20088         if(this.preventDefault){
20089             e.preventDefault();
20090         }
20091         
20092         this.fireEvent("click", this, e);
20093     },
20094     
20095     onMouseOver : function(e)
20096     {
20097         if(this.submenu && this.pos == 'left'){
20098             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
20099         }
20100         
20101         this.fireEvent("mouseover", this, e);
20102     },
20103     
20104     onMouseOut : function(e)
20105     {
20106         this.fireEvent("mouseout", this, e);
20107     }
20108 });
20109
20110  
20111
20112  /*
20113  * - LGPL
20114  *
20115  * menu separator
20116  * 
20117  */
20118 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20119
20120 /**
20121  * @class Roo.bootstrap.menu.Separator
20122  * @extends Roo.bootstrap.Component
20123  * Bootstrap Separator class
20124  * 
20125  * @constructor
20126  * Create a new Separator
20127  * @param {Object} config The config object
20128  */
20129
20130
20131 Roo.bootstrap.menu.Separator = function(config){
20132     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
20133 };
20134
20135 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
20136     
20137     getAutoCreate : function(){
20138         var cfg = {
20139             tag : 'li',
20140             cls: 'divider'
20141         };
20142         
20143         return cfg;
20144     }
20145    
20146 });
20147
20148  
20149
20150