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         if(ds.getCount() > 0){
5162             ds.data.each(function(d,rowIndex){
5163                 var row =  this.renderRow(cm, ds, rowIndex);
5164                 
5165                 tbody.createChild(row);
5166                 
5167                 var _this = this;
5168                 
5169                 if(row.cellObjects.length){
5170                     Roo.each(row.cellObjects, function(r){
5171                         _this.renderCellObject(r);
5172                     })
5173                 }
5174                 
5175             }, this);
5176         }
5177         
5178         Roo.each(this.el.select('tbody td', true).elements, function(e){
5179             e.on('mouseover', _this.onMouseover, _this);
5180         });
5181         
5182         Roo.each(this.el.select('tbody td', true).elements, function(e){
5183             e.on('mouseout', _this.onMouseout, _this);
5184         });
5185
5186         //if(this.loadMask){
5187         //    this.maskEl.hide();
5188         //}
5189     },
5190     
5191     
5192     onUpdate : function(ds,record)
5193     {
5194         this.refreshRow(record);
5195     },
5196     onRemove : function(ds, record, index, isUpdate){
5197         if(isUpdate !== true){
5198             this.fireEvent("beforerowremoved", this, index, record);
5199         }
5200         var bt = this.mainBody.dom;
5201         if(bt.rows[index]){
5202             bt.removeChild(bt.rows[index]);
5203         }
5204         
5205         if(isUpdate !== true){
5206             //this.stripeRows(index);
5207             //this.syncRowHeights(index, index);
5208             //this.layout();
5209             this.fireEvent("rowremoved", this, index, record);
5210         }
5211     },
5212     
5213     
5214     refreshRow : function(record){
5215         var ds = this.store, index;
5216         if(typeof record == 'number'){
5217             index = record;
5218             record = ds.getAt(index);
5219         }else{
5220             index = ds.indexOf(record);
5221         }
5222         this.insertRow(ds, index, true);
5223         this.onRemove(ds, record, index+1, true);
5224         //this.syncRowHeights(index, index);
5225         //this.layout();
5226         this.fireEvent("rowupdated", this, index, record);
5227     },
5228     
5229     insertRow : function(dm, rowIndex, isUpdate){
5230         
5231         if(!isUpdate){
5232             this.fireEvent("beforerowsinserted", this, rowIndex);
5233         }
5234             //var s = this.getScrollState();
5235         var row = this.renderRow(this.cm, this.store, rowIndex);
5236         // insert before rowIndex..
5237         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
5238         
5239         var _this = this;
5240                 
5241         if(row.cellObjects.length){
5242             Roo.each(row.cellObjects, function(r){
5243                 _this.renderCellObject(r);
5244             })
5245         }
5246             
5247         if(!isUpdate){
5248             this.fireEvent("rowsinserted", this, rowIndex);
5249             //this.syncRowHeights(firstRow, lastRow);
5250             //this.stripeRows(firstRow);
5251             //this.layout();
5252         }
5253         
5254     },
5255     
5256     
5257     getRowDom : function(rowIndex)
5258     {
5259         // not sure if I need to check this.. but let's do it anyway..
5260         return (this.mainBody.dom.rows && (rowIndex-1) < this.mainBody.dom.rows.length ) ?
5261                 this.mainBody.dom.rows[rowIndex] : false
5262     },
5263     // returns the object tree for a tr..
5264   
5265     
5266     renderRow : function(cm, ds, rowIndex) {
5267         
5268         var d = ds.getAt(rowIndex);
5269         
5270         var row = {
5271             tag : 'tr',
5272             cn : []
5273         };
5274             
5275         var cellObjects = [];
5276         
5277         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5278             var config = cm.config[i];
5279             
5280             var renderer = cm.getRenderer(i);
5281             var value = '';
5282             var id = false;
5283             
5284             if(typeof(renderer) !== 'undefined'){
5285                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
5286             }
5287             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
5288             // and are rendered into the cells after the row is rendered - using the id for the element.
5289             
5290             if(typeof(value) === 'object'){
5291                 id = Roo.id();
5292                 cellObjects.push({
5293                     container : id,
5294                     cfg : value 
5295                 })
5296             }
5297             
5298             var rowcfg = {
5299                 record: d,
5300                 rowIndex : rowIndex,
5301                 colIndex : i,
5302                 rowClass : ''
5303             }
5304
5305             this.fireEvent('rowclass', this, rowcfg);
5306             
5307             var td = {
5308                 tag: 'td',
5309                 cls : rowcfg.rowClass,
5310                 style: '',
5311                 html: (typeof(value) === 'object') ? '' : value
5312             };
5313             
5314             if (id) {
5315                 td.id = id;
5316             }
5317             
5318             if(typeof(config.hidden) != 'undefined' && config.hidden){
5319                 td.style += ' display:none;';
5320             }
5321             
5322             if(typeof(config.align) != 'undefined' && config.align.length){
5323                 td.style += ' text-align:' + config.align + ';';
5324             }
5325             
5326             if(typeof(config.width) != 'undefined'){
5327                 td.style += ' width:' +  config.width + 'px;';
5328             }
5329              
5330             row.cn.push(td);
5331            
5332         }
5333         
5334         row.cellObjects = cellObjects;
5335         
5336         return row;
5337           
5338     },
5339     
5340     
5341     
5342     onBeforeLoad : function()
5343     {
5344         //Roo.log('ds onBeforeLoad');
5345         
5346         //this.clear();
5347         
5348         //if(this.loadMask){
5349         //    this.maskEl.show();
5350         //}
5351     },
5352     
5353     clear : function()
5354     {
5355         this.el.select('tbody', true).first().dom.innerHTML = '';
5356     },
5357     
5358     getSelectionModel : function(){
5359         if(!this.selModel){
5360             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
5361         }
5362         return this.selModel;
5363     },
5364     /*
5365      * Render the Roo.bootstrap object from renderder
5366      */
5367     renderCellObject : function(r)
5368     {
5369         var _this = this;
5370         
5371         var t = r.cfg.render(r.container);
5372         
5373         if(r.cfg.cn){
5374             Roo.each(r.cfg.cn, function(c){
5375                 var child = {
5376                     container: t.getChildContainer(),
5377                     cfg: c
5378                 }
5379                 _this.renderCellObject(child);
5380             })
5381         }
5382     }
5383    
5384 });
5385
5386  
5387
5388  /*
5389  * - LGPL
5390  *
5391  * table cell
5392  * 
5393  */
5394
5395 /**
5396  * @class Roo.bootstrap.TableCell
5397  * @extends Roo.bootstrap.Component
5398  * Bootstrap TableCell class
5399  * @cfg {String} html cell contain text
5400  * @cfg {String} cls cell class
5401  * @cfg {String} tag cell tag (td|th) default td
5402  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
5403  * @cfg {String} align Aligns the content in a cell
5404  * @cfg {String} axis Categorizes cells
5405  * @cfg {String} bgcolor Specifies the background color of a cell
5406  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5407  * @cfg {Number} colspan Specifies the number of columns a cell should span
5408  * @cfg {String} headers Specifies one or more header cells a cell is related to
5409  * @cfg {Number} height Sets the height of a cell
5410  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
5411  * @cfg {Number} rowspan Sets the number of rows a cell should span
5412  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
5413  * @cfg {String} valign Vertical aligns the content in a cell
5414  * @cfg {Number} width Specifies the width of a cell
5415  * 
5416  * @constructor
5417  * Create a new TableCell
5418  * @param {Object} config The config object
5419  */
5420
5421 Roo.bootstrap.TableCell = function(config){
5422     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
5423 };
5424
5425 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
5426     
5427     html: false,
5428     cls: false,
5429     tag: false,
5430     abbr: false,
5431     align: false,
5432     axis: false,
5433     bgcolor: false,
5434     charoff: false,
5435     colspan: false,
5436     headers: false,
5437     height: false,
5438     nowrap: false,
5439     rowspan: false,
5440     scope: false,
5441     valign: false,
5442     width: false,
5443     
5444     
5445     getAutoCreate : function(){
5446         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
5447         
5448         cfg = {
5449             tag: 'td'
5450         }
5451         
5452         if(this.tag){
5453             cfg.tag = this.tag;
5454         }
5455         
5456         if (this.html) {
5457             cfg.html=this.html
5458         }
5459         if (this.cls) {
5460             cfg.cls=this.cls
5461         }
5462         if (this.abbr) {
5463             cfg.abbr=this.abbr
5464         }
5465         if (this.align) {
5466             cfg.align=this.align
5467         }
5468         if (this.axis) {
5469             cfg.axis=this.axis
5470         }
5471         if (this.bgcolor) {
5472             cfg.bgcolor=this.bgcolor
5473         }
5474         if (this.charoff) {
5475             cfg.charoff=this.charoff
5476         }
5477         if (this.colspan) {
5478             cfg.colspan=this.colspan
5479         }
5480         if (this.headers) {
5481             cfg.headers=this.headers
5482         }
5483         if (this.height) {
5484             cfg.height=this.height
5485         }
5486         if (this.nowrap) {
5487             cfg.nowrap=this.nowrap
5488         }
5489         if (this.rowspan) {
5490             cfg.rowspan=this.rowspan
5491         }
5492         if (this.scope) {
5493             cfg.scope=this.scope
5494         }
5495         if (this.valign) {
5496             cfg.valign=this.valign
5497         }
5498         if (this.width) {
5499             cfg.width=this.width
5500         }
5501         
5502         
5503         return cfg;
5504     }
5505    
5506 });
5507
5508  
5509
5510  /*
5511  * - LGPL
5512  *
5513  * table row
5514  * 
5515  */
5516
5517 /**
5518  * @class Roo.bootstrap.TableRow
5519  * @extends Roo.bootstrap.Component
5520  * Bootstrap TableRow class
5521  * @cfg {String} cls row class
5522  * @cfg {String} align Aligns the content in a table row
5523  * @cfg {String} bgcolor Specifies a background color for a table row
5524  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5525  * @cfg {String} valign Vertical aligns the content in a table row
5526  * 
5527  * @constructor
5528  * Create a new TableRow
5529  * @param {Object} config The config object
5530  */
5531
5532 Roo.bootstrap.TableRow = function(config){
5533     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
5534 };
5535
5536 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
5537     
5538     cls: false,
5539     align: false,
5540     bgcolor: false,
5541     charoff: false,
5542     valign: false,
5543     
5544     getAutoCreate : function(){
5545         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
5546         
5547         cfg = {
5548             tag: 'tr'
5549         }
5550             
5551         if(this.cls){
5552             cfg.cls = this.cls;
5553         }
5554         if(this.align){
5555             cfg.align = this.align;
5556         }
5557         if(this.bgcolor){
5558             cfg.bgcolor = this.bgcolor;
5559         }
5560         if(this.charoff){
5561             cfg.charoff = this.charoff;
5562         }
5563         if(this.valign){
5564             cfg.valign = this.valign;
5565         }
5566         
5567         return cfg;
5568     }
5569    
5570 });
5571
5572  
5573
5574  /*
5575  * - LGPL
5576  *
5577  * table body
5578  * 
5579  */
5580
5581 /**
5582  * @class Roo.bootstrap.TableBody
5583  * @extends Roo.bootstrap.Component
5584  * Bootstrap TableBody class
5585  * @cfg {String} cls element class
5586  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
5587  * @cfg {String} align Aligns the content inside the element
5588  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
5589  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
5590  * 
5591  * @constructor
5592  * Create a new TableBody
5593  * @param {Object} config The config object
5594  */
5595
5596 Roo.bootstrap.TableBody = function(config){
5597     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
5598 };
5599
5600 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
5601     
5602     cls: false,
5603     tag: false,
5604     align: false,
5605     charoff: false,
5606     valign: false,
5607     
5608     getAutoCreate : function(){
5609         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
5610         
5611         cfg = {
5612             tag: 'tbody'
5613         }
5614             
5615         if (this.cls) {
5616             cfg.cls=this.cls
5617         }
5618         if(this.tag){
5619             cfg.tag = this.tag;
5620         }
5621         
5622         if(this.align){
5623             cfg.align = this.align;
5624         }
5625         if(this.charoff){
5626             cfg.charoff = this.charoff;
5627         }
5628         if(this.valign){
5629             cfg.valign = this.valign;
5630         }
5631         
5632         return cfg;
5633     }
5634     
5635     
5636 //    initEvents : function()
5637 //    {
5638 //        
5639 //        if(!this.store){
5640 //            return;
5641 //        }
5642 //        
5643 //        this.store = Roo.factory(this.store, Roo.data);
5644 //        this.store.on('load', this.onLoad, this);
5645 //        
5646 //        this.store.load();
5647 //        
5648 //    },
5649 //    
5650 //    onLoad: function () 
5651 //    {   
5652 //        this.fireEvent('load', this);
5653 //    }
5654 //    
5655 //   
5656 });
5657
5658  
5659
5660  /*
5661  * Based on:
5662  * Ext JS Library 1.1.1
5663  * Copyright(c) 2006-2007, Ext JS, LLC.
5664  *
5665  * Originally Released Under LGPL - original licence link has changed is not relivant.
5666  *
5667  * Fork - LGPL
5668  * <script type="text/javascript">
5669  */
5670
5671 // as we use this in bootstrap.
5672 Roo.namespace('Roo.form');
5673  /**
5674  * @class Roo.form.Action
5675  * Internal Class used to handle form actions
5676  * @constructor
5677  * @param {Roo.form.BasicForm} el The form element or its id
5678  * @param {Object} config Configuration options
5679  */
5680
5681  
5682  
5683 // define the action interface
5684 Roo.form.Action = function(form, options){
5685     this.form = form;
5686     this.options = options || {};
5687 };
5688 /**
5689  * Client Validation Failed
5690  * @const 
5691  */
5692 Roo.form.Action.CLIENT_INVALID = 'client';
5693 /**
5694  * Server Validation Failed
5695  * @const 
5696  */
5697 Roo.form.Action.SERVER_INVALID = 'server';
5698  /**
5699  * Connect to Server Failed
5700  * @const 
5701  */
5702 Roo.form.Action.CONNECT_FAILURE = 'connect';
5703 /**
5704  * Reading Data from Server Failed
5705  * @const 
5706  */
5707 Roo.form.Action.LOAD_FAILURE = 'load';
5708
5709 Roo.form.Action.prototype = {
5710     type : 'default',
5711     failureType : undefined,
5712     response : undefined,
5713     result : undefined,
5714
5715     // interface method
5716     run : function(options){
5717
5718     },
5719
5720     // interface method
5721     success : function(response){
5722
5723     },
5724
5725     // interface method
5726     handleResponse : function(response){
5727
5728     },
5729
5730     // default connection failure
5731     failure : function(response){
5732         
5733         this.response = response;
5734         this.failureType = Roo.form.Action.CONNECT_FAILURE;
5735         this.form.afterAction(this, false);
5736     },
5737
5738     processResponse : function(response){
5739         this.response = response;
5740         if(!response.responseText){
5741             return true;
5742         }
5743         this.result = this.handleResponse(response);
5744         return this.result;
5745     },
5746
5747     // utility functions used internally
5748     getUrl : function(appendParams){
5749         var url = this.options.url || this.form.url || this.form.el.dom.action;
5750         if(appendParams){
5751             var p = this.getParams();
5752             if(p){
5753                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
5754             }
5755         }
5756         return url;
5757     },
5758
5759     getMethod : function(){
5760         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
5761     },
5762
5763     getParams : function(){
5764         var bp = this.form.baseParams;
5765         var p = this.options.params;
5766         if(p){
5767             if(typeof p == "object"){
5768                 p = Roo.urlEncode(Roo.applyIf(p, bp));
5769             }else if(typeof p == 'string' && bp){
5770                 p += '&' + Roo.urlEncode(bp);
5771             }
5772         }else if(bp){
5773             p = Roo.urlEncode(bp);
5774         }
5775         return p;
5776     },
5777
5778     createCallback : function(){
5779         return {
5780             success: this.success,
5781             failure: this.failure,
5782             scope: this,
5783             timeout: (this.form.timeout*1000),
5784             upload: this.form.fileUpload ? this.success : undefined
5785         };
5786     }
5787 };
5788
5789 Roo.form.Action.Submit = function(form, options){
5790     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
5791 };
5792
5793 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
5794     type : 'submit',
5795
5796     haveProgress : false,
5797     uploadComplete : false,
5798     
5799     // uploadProgress indicator.
5800     uploadProgress : function()
5801     {
5802         if (!this.form.progressUrl) {
5803             return;
5804         }
5805         
5806         if (!this.haveProgress) {
5807             Roo.MessageBox.progress("Uploading", "Uploading");
5808         }
5809         if (this.uploadComplete) {
5810            Roo.MessageBox.hide();
5811            return;
5812         }
5813         
5814         this.haveProgress = true;
5815    
5816         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
5817         
5818         var c = new Roo.data.Connection();
5819         c.request({
5820             url : this.form.progressUrl,
5821             params: {
5822                 id : uid
5823             },
5824             method: 'GET',
5825             success : function(req){
5826                //console.log(data);
5827                 var rdata = false;
5828                 var edata;
5829                 try  {
5830                    rdata = Roo.decode(req.responseText)
5831                 } catch (e) {
5832                     Roo.log("Invalid data from server..");
5833                     Roo.log(edata);
5834                     return;
5835                 }
5836                 if (!rdata || !rdata.success) {
5837                     Roo.log(rdata);
5838                     Roo.MessageBox.alert(Roo.encode(rdata));
5839                     return;
5840                 }
5841                 var data = rdata.data;
5842                 
5843                 if (this.uploadComplete) {
5844                    Roo.MessageBox.hide();
5845                    return;
5846                 }
5847                    
5848                 if (data){
5849                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
5850                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
5851                     );
5852                 }
5853                 this.uploadProgress.defer(2000,this);
5854             },
5855        
5856             failure: function(data) {
5857                 Roo.log('progress url failed ');
5858                 Roo.log(data);
5859             },
5860             scope : this
5861         });
5862            
5863     },
5864     
5865     
5866     run : function()
5867     {
5868         // run get Values on the form, so it syncs any secondary forms.
5869         this.form.getValues();
5870         
5871         var o = this.options;
5872         var method = this.getMethod();
5873         var isPost = method == 'POST';
5874         if(o.clientValidation === false || this.form.isValid()){
5875             
5876             if (this.form.progressUrl) {
5877                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
5878                     (new Date() * 1) + '' + Math.random());
5879                     
5880             } 
5881             
5882             
5883             Roo.Ajax.request(Roo.apply(this.createCallback(), {
5884                 form:this.form.el.dom,
5885                 url:this.getUrl(!isPost),
5886                 method: method,
5887                 params:isPost ? this.getParams() : null,
5888                 isUpload: this.form.fileUpload
5889             }));
5890             
5891             this.uploadProgress();
5892
5893         }else if (o.clientValidation !== false){ // client validation failed
5894             this.failureType = Roo.form.Action.CLIENT_INVALID;
5895             this.form.afterAction(this, false);
5896         }
5897     },
5898
5899     success : function(response)
5900     {
5901         this.uploadComplete= true;
5902         if (this.haveProgress) {
5903             Roo.MessageBox.hide();
5904         }
5905         
5906         
5907         var result = this.processResponse(response);
5908         if(result === true || result.success){
5909             this.form.afterAction(this, true);
5910             return;
5911         }
5912         if(result.errors){
5913             this.form.markInvalid(result.errors);
5914             this.failureType = Roo.form.Action.SERVER_INVALID;
5915         }
5916         this.form.afterAction(this, false);
5917     },
5918     failure : function(response)
5919     {
5920         this.uploadComplete= true;
5921         if (this.haveProgress) {
5922             Roo.MessageBox.hide();
5923         }
5924         
5925         this.response = response;
5926         this.failureType = Roo.form.Action.CONNECT_FAILURE;
5927         this.form.afterAction(this, false);
5928     },
5929     
5930     handleResponse : function(response){
5931         if(this.form.errorReader){
5932             var rs = this.form.errorReader.read(response);
5933             var errors = [];
5934             if(rs.records){
5935                 for(var i = 0, len = rs.records.length; i < len; i++) {
5936                     var r = rs.records[i];
5937                     errors[i] = r.data;
5938                 }
5939             }
5940             if(errors.length < 1){
5941                 errors = null;
5942             }
5943             return {
5944                 success : rs.success,
5945                 errors : errors
5946             };
5947         }
5948         var ret = false;
5949         try {
5950             ret = Roo.decode(response.responseText);
5951         } catch (e) {
5952             ret = {
5953                 success: false,
5954                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
5955                 errors : []
5956             };
5957         }
5958         return ret;
5959         
5960     }
5961 });
5962
5963
5964 Roo.form.Action.Load = function(form, options){
5965     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
5966     this.reader = this.form.reader;
5967 };
5968
5969 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
5970     type : 'load',
5971
5972     run : function(){
5973         
5974         Roo.Ajax.request(Roo.apply(
5975                 this.createCallback(), {
5976                     method:this.getMethod(),
5977                     url:this.getUrl(false),
5978                     params:this.getParams()
5979         }));
5980     },
5981
5982     success : function(response){
5983         
5984         var result = this.processResponse(response);
5985         if(result === true || !result.success || !result.data){
5986             this.failureType = Roo.form.Action.LOAD_FAILURE;
5987             this.form.afterAction(this, false);
5988             return;
5989         }
5990         this.form.clearInvalid();
5991         this.form.setValues(result.data);
5992         this.form.afterAction(this, true);
5993     },
5994
5995     handleResponse : function(response){
5996         if(this.form.reader){
5997             var rs = this.form.reader.read(response);
5998             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
5999             return {
6000                 success : rs.success,
6001                 data : data
6002             };
6003         }
6004         return Roo.decode(response.responseText);
6005     }
6006 });
6007
6008 Roo.form.Action.ACTION_TYPES = {
6009     'load' : Roo.form.Action.Load,
6010     'submit' : Roo.form.Action.Submit
6011 };/*
6012  * - LGPL
6013  *
6014  * form
6015  * 
6016  */
6017
6018 /**
6019  * @class Roo.bootstrap.Form
6020  * @extends Roo.bootstrap.Component
6021  * Bootstrap Form class
6022  * @cfg {String} method  GET | POST (default POST)
6023  * @cfg {String} labelAlign top | left (default top)
6024   * @cfg {String} align left  | right - for navbars
6025
6026  * 
6027  * @constructor
6028  * Create a new Form
6029  * @param {Object} config The config object
6030  */
6031
6032
6033 Roo.bootstrap.Form = function(config){
6034     Roo.bootstrap.Form.superclass.constructor.call(this, config);
6035     this.addEvents({
6036         /**
6037          * @event clientvalidation
6038          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
6039          * @param {Form} this
6040          * @param {Boolean} valid true if the form has passed client-side validation
6041          */
6042         clientvalidation: true,
6043         /**
6044          * @event beforeaction
6045          * Fires before any action is performed. Return false to cancel the action.
6046          * @param {Form} this
6047          * @param {Action} action The action to be performed
6048          */
6049         beforeaction: true,
6050         /**
6051          * @event actionfailed
6052          * Fires when an action fails.
6053          * @param {Form} this
6054          * @param {Action} action The action that failed
6055          */
6056         actionfailed : true,
6057         /**
6058          * @event actioncomplete
6059          * Fires when an action is completed.
6060          * @param {Form} this
6061          * @param {Action} action The action that completed
6062          */
6063         actioncomplete : true
6064     });
6065     
6066 };
6067
6068 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
6069       
6070      /**
6071      * @cfg {String} method
6072      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
6073      */
6074     method : 'POST',
6075     /**
6076      * @cfg {String} url
6077      * The URL to use for form actions if one isn't supplied in the action options.
6078      */
6079     /**
6080      * @cfg {Boolean} fileUpload
6081      * Set to true if this form is a file upload.
6082      */
6083      
6084     /**
6085      * @cfg {Object} baseParams
6086      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
6087      */
6088       
6089     /**
6090      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
6091      */
6092     timeout: 30,
6093     /**
6094      * @cfg {Sting} align (left|right) for navbar forms
6095      */
6096     align : 'left',
6097
6098     // private
6099     activeAction : null,
6100  
6101     /**
6102      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6103      * element by passing it or its id or mask the form itself by passing in true.
6104      * @type Mixed
6105      */
6106     waitMsgTarget : false,
6107     
6108      
6109     
6110     /**
6111      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6112      * element by passing it or its id or mask the form itself by passing in true.
6113      * @type Mixed
6114      */
6115     
6116     getAutoCreate : function(){
6117         
6118         var cfg = {
6119             tag: 'form',
6120             method : this.method || 'POST',
6121             id : this.id || Roo.id(),
6122             cls : ''
6123         }
6124         if (this.parent().xtype.match(/^Nav/)) {
6125             cfg.cls = 'navbar-form navbar-' + this.align;
6126             
6127         }
6128         
6129         if (this.labelAlign == 'left' ) {
6130             cfg.cls += ' form-horizontal';
6131         }
6132         
6133         
6134         return cfg;
6135     },
6136     initEvents : function()
6137     {
6138         this.el.on('submit', this.onSubmit, this);
6139         // this was added as random key presses on the form where triggering form submit.
6140         this.el.on('keypress', function(e) {
6141             if (e.getCharCode() != 13) {
6142                 return true;
6143             }
6144             // we might need to allow it for textareas.. and some other items.
6145             // check e.getTarget().
6146             
6147             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
6148                 return true;
6149             }
6150         
6151             Roo.log("keypress blocked");
6152             
6153             e.preventDefault();
6154             return false;
6155         });
6156         
6157     },
6158     // private
6159     onSubmit : function(e){
6160         e.stopEvent();
6161     },
6162     
6163      /**
6164      * Returns true if client-side validation on the form is successful.
6165      * @return Boolean
6166      */
6167     isValid : function(){
6168         var items = this.getItems();
6169         var valid = true;
6170         items.each(function(f){
6171            if(!f.validate()){
6172                valid = false;
6173                
6174            }
6175         });
6176         return valid;
6177     },
6178     /**
6179      * Returns true if any fields in this form have changed since their original load.
6180      * @return Boolean
6181      */
6182     isDirty : function(){
6183         var dirty = false;
6184         var items = this.getItems();
6185         items.each(function(f){
6186            if(f.isDirty()){
6187                dirty = true;
6188                return false;
6189            }
6190            return true;
6191         });
6192         return dirty;
6193     },
6194      /**
6195      * Performs a predefined action (submit or load) or custom actions you define on this form.
6196      * @param {String} actionName The name of the action type
6197      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
6198      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
6199      * accept other config options):
6200      * <pre>
6201 Property          Type             Description
6202 ----------------  ---------------  ----------------------------------------------------------------------------------
6203 url               String           The url for the action (defaults to the form's url)
6204 method            String           The form method to use (defaults to the form's method, or POST if not defined)
6205 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
6206 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
6207                                    validate the form on the client (defaults to false)
6208      * </pre>
6209      * @return {BasicForm} this
6210      */
6211     doAction : function(action, options){
6212         if(typeof action == 'string'){
6213             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
6214         }
6215         if(this.fireEvent('beforeaction', this, action) !== false){
6216             this.beforeAction(action);
6217             action.run.defer(100, action);
6218         }
6219         return this;
6220     },
6221     
6222     // private
6223     beforeAction : function(action){
6224         var o = action.options;
6225         
6226         // not really supported yet.. ??
6227         
6228         //if(this.waitMsgTarget === true){
6229             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6230         //}else if(this.waitMsgTarget){
6231         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
6232         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
6233         //}else {
6234         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
6235        // }
6236          
6237     },
6238
6239     // private
6240     afterAction : function(action, success){
6241         this.activeAction = null;
6242         var o = action.options;
6243         
6244         //if(this.waitMsgTarget === true){
6245             this.el.unmask();
6246         //}else if(this.waitMsgTarget){
6247         //    this.waitMsgTarget.unmask();
6248         //}else{
6249         //    Roo.MessageBox.updateProgress(1);
6250         //    Roo.MessageBox.hide();
6251        // }
6252         // 
6253         if(success){
6254             if(o.reset){
6255                 this.reset();
6256             }
6257             Roo.callback(o.success, o.scope, [this, action]);
6258             this.fireEvent('actioncomplete', this, action);
6259             
6260         }else{
6261             
6262             // failure condition..
6263             // we have a scenario where updates need confirming.
6264             // eg. if a locking scenario exists..
6265             // we look for { errors : { needs_confirm : true }} in the response.
6266             if (
6267                 (typeof(action.result) != 'undefined')  &&
6268                 (typeof(action.result.errors) != 'undefined')  &&
6269                 (typeof(action.result.errors.needs_confirm) != 'undefined')
6270            ){
6271                 var _t = this;
6272                 Roo.log("not supported yet");
6273                  /*
6274                 
6275                 Roo.MessageBox.confirm(
6276                     "Change requires confirmation",
6277                     action.result.errorMsg,
6278                     function(r) {
6279                         if (r != 'yes') {
6280                             return;
6281                         }
6282                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
6283                     }
6284                     
6285                 );
6286                 */
6287                 
6288                 
6289                 return;
6290             }
6291             
6292             Roo.callback(o.failure, o.scope, [this, action]);
6293             // show an error message if no failed handler is set..
6294             if (!this.hasListener('actionfailed')) {
6295                 Roo.log("need to add dialog support");
6296                 /*
6297                 Roo.MessageBox.alert("Error",
6298                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
6299                         action.result.errorMsg :
6300                         "Saving Failed, please check your entries or try again"
6301                 );
6302                 */
6303             }
6304             
6305             this.fireEvent('actionfailed', this, action);
6306         }
6307         
6308     },
6309     /**
6310      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
6311      * @param {String} id The value to search for
6312      * @return Field
6313      */
6314     findField : function(id){
6315         var items = this.getItems();
6316         var field = items.get(id);
6317         if(!field){
6318              items.each(function(f){
6319                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
6320                     field = f;
6321                     return false;
6322                 }
6323                 return true;
6324             });
6325         }
6326         return field || null;
6327     },
6328      /**
6329      * Mark fields in this form invalid in bulk.
6330      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
6331      * @return {BasicForm} this
6332      */
6333     markInvalid : function(errors){
6334         if(errors instanceof Array){
6335             for(var i = 0, len = errors.length; i < len; i++){
6336                 var fieldError = errors[i];
6337                 var f = this.findField(fieldError.id);
6338                 if(f){
6339                     f.markInvalid(fieldError.msg);
6340                 }
6341             }
6342         }else{
6343             var field, id;
6344             for(id in errors){
6345                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
6346                     field.markInvalid(errors[id]);
6347                 }
6348             }
6349         }
6350         //Roo.each(this.childForms || [], function (f) {
6351         //    f.markInvalid(errors);
6352         //});
6353         
6354         return this;
6355     },
6356
6357     /**
6358      * Set values for fields in this form in bulk.
6359      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
6360      * @return {BasicForm} this
6361      */
6362     setValues : function(values){
6363         if(values instanceof Array){ // array of objects
6364             for(var i = 0, len = values.length; i < len; i++){
6365                 var v = values[i];
6366                 var f = this.findField(v.id);
6367                 if(f){
6368                     f.setValue(v.value);
6369                     if(this.trackResetOnLoad){
6370                         f.originalValue = f.getValue();
6371                     }
6372                 }
6373             }
6374         }else{ // object hash
6375             var field, id;
6376             for(id in values){
6377                 if(typeof values[id] != 'function' && (field = this.findField(id))){
6378                     
6379                     if (field.setFromData && 
6380                         field.valueField && 
6381                         field.displayField &&
6382                         // combos' with local stores can 
6383                         // be queried via setValue()
6384                         // to set their value..
6385                         (field.store && !field.store.isLocal)
6386                         ) {
6387                         // it's a combo
6388                         var sd = { };
6389                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
6390                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
6391                         field.setFromData(sd);
6392                         
6393                     } else {
6394                         field.setValue(values[id]);
6395                     }
6396                     
6397                     
6398                     if(this.trackResetOnLoad){
6399                         field.originalValue = field.getValue();
6400                     }
6401                 }
6402             }
6403         }
6404          
6405         //Roo.each(this.childForms || [], function (f) {
6406         //    f.setValues(values);
6407         //});
6408                 
6409         return this;
6410     },
6411
6412     /**
6413      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
6414      * they are returned as an array.
6415      * @param {Boolean} asString
6416      * @return {Object}
6417      */
6418     getValues : function(asString){
6419         //if (this.childForms) {
6420             // copy values from the child forms
6421         //    Roo.each(this.childForms, function (f) {
6422         //        this.setValues(f.getValues());
6423         //    }, this);
6424         //}
6425         
6426         
6427         
6428         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
6429         if(asString === true){
6430             return fs;
6431         }
6432         return Roo.urlDecode(fs);
6433     },
6434     
6435     /**
6436      * Returns the fields in this form as an object with key/value pairs. 
6437      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
6438      * @return {Object}
6439      */
6440     getFieldValues : function(with_hidden)
6441     {
6442         var items = this.getItems();
6443         var ret = {};
6444         items.each(function(f){
6445             if (!f.getName()) {
6446                 return;
6447             }
6448             var v = f.getValue();
6449             if (f.inputType =='radio') {
6450                 if (typeof(ret[f.getName()]) == 'undefined') {
6451                     ret[f.getName()] = ''; // empty..
6452                 }
6453                 
6454                 if (!f.el.dom.checked) {
6455                     return;
6456                     
6457                 }
6458                 v = f.el.dom.value;
6459                 
6460             }
6461             
6462             // not sure if this supported any more..
6463             if ((typeof(v) == 'object') && f.getRawValue) {
6464                 v = f.getRawValue() ; // dates..
6465             }
6466             // combo boxes where name != hiddenName...
6467             if (f.name != f.getName()) {
6468                 ret[f.name] = f.getRawValue();
6469             }
6470             ret[f.getName()] = v;
6471         });
6472         
6473         return ret;
6474     },
6475
6476     /**
6477      * Clears all invalid messages in this form.
6478      * @return {BasicForm} this
6479      */
6480     clearInvalid : function(){
6481         var items = this.getItems();
6482         
6483         items.each(function(f){
6484            f.clearInvalid();
6485         });
6486         
6487         
6488         
6489         return this;
6490     },
6491
6492     /**
6493      * Resets this form.
6494      * @return {BasicForm} this
6495      */
6496     reset : function(){
6497         var items = this.getItems();
6498         items.each(function(f){
6499             f.reset();
6500         });
6501         
6502         Roo.each(this.childForms || [], function (f) {
6503             f.reset();
6504         });
6505        
6506         
6507         return this;
6508     },
6509     getItems : function()
6510     {
6511         var r=new Roo.util.MixedCollection(false, function(o){
6512             return o.id || (o.id = Roo.id());
6513         });
6514         var iter = function(el) {
6515             if (el.inputEl) {
6516                 r.add(el);
6517             }
6518             if (!el.items) {
6519                 return;
6520             }
6521             Roo.each(el.items,function(e) {
6522                 iter(e);
6523             });
6524             
6525             
6526         };
6527         iter(this);
6528         return r;
6529         
6530         
6531         
6532         
6533     }
6534     
6535 });
6536
6537  
6538 /*
6539  * Based on:
6540  * Ext JS Library 1.1.1
6541  * Copyright(c) 2006-2007, Ext JS, LLC.
6542  *
6543  * Originally Released Under LGPL - original licence link has changed is not relivant.
6544  *
6545  * Fork - LGPL
6546  * <script type="text/javascript">
6547  */
6548 /**
6549  * @class Roo.form.VTypes
6550  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
6551  * @singleton
6552  */
6553 Roo.form.VTypes = function(){
6554     // closure these in so they are only created once.
6555     var alpha = /^[a-zA-Z_]+$/;
6556     var alphanum = /^[a-zA-Z0-9_]+$/;
6557     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
6558     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
6559
6560     // All these messages and functions are configurable
6561     return {
6562         /**
6563          * The function used to validate email addresses
6564          * @param {String} value The email address
6565          */
6566         'email' : function(v){
6567             return email.test(v);
6568         },
6569         /**
6570          * The error text to display when the email validation function returns false
6571          * @type String
6572          */
6573         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
6574         /**
6575          * The keystroke filter mask to be applied on email input
6576          * @type RegExp
6577          */
6578         'emailMask' : /[a-z0-9_\.\-@]/i,
6579
6580         /**
6581          * The function used to validate URLs
6582          * @param {String} value The URL
6583          */
6584         'url' : function(v){
6585             return url.test(v);
6586         },
6587         /**
6588          * The error text to display when the url validation function returns false
6589          * @type String
6590          */
6591         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
6592         
6593         /**
6594          * The function used to validate alpha values
6595          * @param {String} value The value
6596          */
6597         'alpha' : function(v){
6598             return alpha.test(v);
6599         },
6600         /**
6601          * The error text to display when the alpha validation function returns false
6602          * @type String
6603          */
6604         'alphaText' : 'This field should only contain letters and _',
6605         /**
6606          * The keystroke filter mask to be applied on alpha input
6607          * @type RegExp
6608          */
6609         'alphaMask' : /[a-z_]/i,
6610
6611         /**
6612          * The function used to validate alphanumeric values
6613          * @param {String} value The value
6614          */
6615         'alphanum' : function(v){
6616             return alphanum.test(v);
6617         },
6618         /**
6619          * The error text to display when the alphanumeric validation function returns false
6620          * @type String
6621          */
6622         'alphanumText' : 'This field should only contain letters, numbers and _',
6623         /**
6624          * The keystroke filter mask to be applied on alphanumeric input
6625          * @type RegExp
6626          */
6627         'alphanumMask' : /[a-z0-9_]/i
6628     };
6629 }();/*
6630  * - LGPL
6631  *
6632  * Input
6633  * 
6634  */
6635
6636 /**
6637  * @class Roo.bootstrap.Input
6638  * @extends Roo.bootstrap.Component
6639  * Bootstrap Input class
6640  * @cfg {Boolean} disabled is it disabled
6641  * @cfg {String} fieldLabel - the label associated
6642  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
6643  * @cfg {String} name name of the input
6644  * @cfg {string} fieldLabel - the label associated
6645  * @cfg {string}  inputType - input / file submit ...
6646  * @cfg {string} placeholder - placeholder to put in text.
6647  * @cfg {string}  before - input group add on before
6648  * @cfg {string} after - input group add on after
6649  * @cfg {string} size - (lg|sm) or leave empty..
6650  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
6651  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
6652  * @cfg {Number} md colspan out of 12 for computer-sized screens
6653  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
6654  * @cfg {string} value default value of the input
6655  * @cfg {Number} labelWidth set the width of label (0-12)
6656  * @cfg {String} labelAlign (top|left)
6657  * @cfg {Boolean} readOnly Specifies that the field should be read-only
6658  * @cfg {String} align (left|center|right) Default left
6659  * 
6660  * 
6661  * @constructor
6662  * Create a new Input
6663  * @param {Object} config The config object
6664  */
6665
6666 Roo.bootstrap.Input = function(config){
6667     Roo.bootstrap.Input.superclass.constructor.call(this, config);
6668    
6669         this.addEvents({
6670             /**
6671              * @event focus
6672              * Fires when this field receives input focus.
6673              * @param {Roo.form.Field} this
6674              */
6675             focus : true,
6676             /**
6677              * @event blur
6678              * Fires when this field loses input focus.
6679              * @param {Roo.form.Field} this
6680              */
6681             blur : true,
6682             /**
6683              * @event specialkey
6684              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
6685              * {@link Roo.EventObject#getKey} to determine which key was pressed.
6686              * @param {Roo.form.Field} this
6687              * @param {Roo.EventObject} e The event object
6688              */
6689             specialkey : true,
6690             /**
6691              * @event change
6692              * Fires just before the field blurs if the field value has changed.
6693              * @param {Roo.form.Field} this
6694              * @param {Mixed} newValue The new value
6695              * @param {Mixed} oldValue The original value
6696              */
6697             change : true,
6698             /**
6699              * @event invalid
6700              * Fires after the field has been marked as invalid.
6701              * @param {Roo.form.Field} this
6702              * @param {String} msg The validation message
6703              */
6704             invalid : true,
6705             /**
6706              * @event valid
6707              * Fires after the field has been validated with no errors.
6708              * @param {Roo.form.Field} this
6709              */
6710             valid : true,
6711              /**
6712              * @event keyup
6713              * Fires after the key up
6714              * @param {Roo.form.Field} this
6715              * @param {Roo.EventObject}  e The event Object
6716              */
6717             keyup : true
6718         });
6719 };
6720
6721 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
6722      /**
6723      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
6724       automatic validation (defaults to "keyup").
6725      */
6726     validationEvent : "keyup",
6727      /**
6728      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
6729      */
6730     validateOnBlur : true,
6731     /**
6732      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
6733      */
6734     validationDelay : 250,
6735      /**
6736      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
6737      */
6738     focusClass : "x-form-focus",  // not needed???
6739     
6740        
6741     /**
6742      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
6743      */
6744     invalidClass : "has-error",
6745     
6746     /**
6747      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
6748      */
6749     selectOnFocus : false,
6750     
6751      /**
6752      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
6753      */
6754     maskRe : null,
6755        /**
6756      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
6757      */
6758     vtype : null,
6759     
6760       /**
6761      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
6762      */
6763     disableKeyFilter : false,
6764     
6765        /**
6766      * @cfg {Boolean} disabled True to disable the field (defaults to false).
6767      */
6768     disabled : false,
6769      /**
6770      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
6771      */
6772     allowBlank : true,
6773     /**
6774      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
6775      */
6776     blankText : "This field is required",
6777     
6778      /**
6779      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
6780      */
6781     minLength : 0,
6782     /**
6783      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
6784      */
6785     maxLength : Number.MAX_VALUE,
6786     /**
6787      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
6788      */
6789     minLengthText : "The minimum length for this field is {0}",
6790     /**
6791      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
6792      */
6793     maxLengthText : "The maximum length for this field is {0}",
6794   
6795     
6796     /**
6797      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
6798      * If available, this function will be called only after the basic validators all return true, and will be passed the
6799      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
6800      */
6801     validator : null,
6802     /**
6803      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
6804      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
6805      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
6806      */
6807     regex : null,
6808     /**
6809      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
6810      */
6811     regexText : "",
6812     
6813     
6814     
6815     fieldLabel : '',
6816     inputType : 'text',
6817     
6818     name : false,
6819     placeholder: false,
6820     before : false,
6821     after : false,
6822     size : false,
6823     // private
6824     hasFocus : false,
6825     preventMark: false,
6826     isFormField : true,
6827     value : '',
6828     labelWidth : 2,
6829     labelAlign : false,
6830     readOnly : false,
6831     align : false,
6832     formatedValue : false,
6833     
6834     parentLabelAlign : function()
6835     {
6836         var parent = this;
6837         while (parent.parent()) {
6838             parent = parent.parent();
6839             if (typeof(parent.labelAlign) !='undefined') {
6840                 return parent.labelAlign;
6841             }
6842         }
6843         return 'left';
6844         
6845     },
6846     
6847     getAutoCreate : function(){
6848         
6849         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
6850         
6851         var id = Roo.id();
6852         
6853         var cfg = {};
6854         
6855         if(this.inputType != 'hidden'){
6856             cfg.cls = 'form-group' //input-group
6857         }
6858         
6859         var input =  {
6860             tag: 'input',
6861             id : id,
6862             type : this.inputType,
6863             value : this.value,
6864             cls : 'form-control',
6865             placeholder : this.placeholder || ''
6866             
6867         };
6868         
6869         if(this.align){
6870             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
6871         }
6872         
6873         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
6874             input.maxLength = this.maxLength;
6875         }
6876         
6877         if (this.disabled) {
6878             input.disabled=true;
6879         }
6880         
6881         if (this.readOnly) {
6882             input.readonly=true;
6883         }
6884         
6885         if (this.name) {
6886             input.name = this.name;
6887         }
6888         if (this.size) {
6889             input.cls += ' input-' + this.size;
6890         }
6891         var settings=this;
6892         ['xs','sm','md','lg'].map(function(size){
6893             if (settings[size]) {
6894                 cfg.cls += ' col-' + size + '-' + settings[size];
6895             }
6896         });
6897         
6898         var inputblock = input;
6899         
6900         if (this.before || this.after) {
6901             
6902             inputblock = {
6903                 cls : 'input-group',
6904                 cn :  [] 
6905             };
6906             if (this.before && typeof(this.before) == 'string') {
6907                 
6908                 inputblock.cn.push({
6909                     tag :'span',
6910                     cls : 'roo-input-before input-group-addon',
6911                     html : this.before
6912                 });
6913             }
6914             if (this.before && typeof(this.before) == 'object') {
6915                 this.before = Roo.factory(this.before);
6916                 Roo.log(this.before);
6917                 inputblock.cn.push({
6918                     tag :'span',
6919                     cls : 'roo-input-before input-group-' +
6920                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
6921                 });
6922             }
6923             
6924             inputblock.cn.push(input);
6925             
6926             if (this.after && typeof(this.after) == 'string') {
6927                 inputblock.cn.push({
6928                     tag :'span',
6929                     cls : 'roo-input-after input-group-addon',
6930                     html : this.after
6931                 });
6932             }
6933             if (this.after && typeof(this.after) == 'object') {
6934                 this.after = Roo.factory(this.after);
6935                 Roo.log(this.after);
6936                 inputblock.cn.push({
6937                     tag :'span',
6938                     cls : 'roo-input-after input-group-' +
6939                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
6940                 });
6941             }
6942         };
6943         
6944         if (align ==='left' && this.fieldLabel.length) {
6945                 Roo.log("left and has label");
6946                 cfg.cn = [
6947                     
6948                     {
6949                         tag: 'label',
6950                         'for' :  id,
6951                         cls : 'control-label col-sm-' + this.labelWidth,
6952                         html : this.fieldLabel
6953                         
6954                     },
6955                     {
6956                         cls : "col-sm-" + (12 - this.labelWidth), 
6957                         cn: [
6958                             inputblock
6959                         ]
6960                     }
6961                     
6962                 ];
6963         } else if ( this.fieldLabel.length) {
6964                 Roo.log(" label");
6965                  cfg.cn = [
6966                    
6967                     {
6968                         tag: 'label',
6969                         //cls : 'input-group-addon',
6970                         html : this.fieldLabel
6971                         
6972                     },
6973                     
6974                     inputblock
6975                     
6976                 ];
6977
6978         } else {
6979             
6980                 Roo.log(" no label && no align");
6981                 cfg.cn = [
6982                     
6983                         inputblock
6984                     
6985                 ];
6986                 
6987                 
6988         };
6989         Roo.log('input-parentType: ' + this.parentType);
6990         
6991         if (this.parentType === 'Navbar' &&  this.parent().bar) {
6992            cfg.cls += ' navbar-form';
6993            Roo.log(cfg);
6994         }
6995         
6996         return cfg;
6997         
6998     },
6999     /**
7000      * return the real input element.
7001      */
7002     inputEl: function ()
7003     {
7004         return this.el.select('input.form-control',true).first();
7005     },
7006     setDisabled : function(v)
7007     {
7008         var i  = this.inputEl().dom;
7009         if (!v) {
7010             i.removeAttribute('disabled');
7011             return;
7012             
7013         }
7014         i.setAttribute('disabled','true');
7015     },
7016     initEvents : function()
7017     {
7018         
7019         this.inputEl().on("keydown" , this.fireKey,  this);
7020         this.inputEl().on("focus", this.onFocus,  this);
7021         this.inputEl().on("blur", this.onBlur,  this);
7022         
7023         this.inputEl().relayEvent('keyup', this);
7024
7025         // reference to original value for reset
7026         this.originalValue = this.getValue();
7027         //Roo.form.TextField.superclass.initEvents.call(this);
7028         if(this.validationEvent == 'keyup'){
7029             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
7030             this.inputEl().on('keyup', this.filterValidation, this);
7031         }
7032         else if(this.validationEvent !== false){
7033             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
7034         }
7035         
7036         if(this.selectOnFocus){
7037             this.on("focus", this.preFocus, this);
7038             
7039         }
7040         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
7041             this.inputEl().on("keypress", this.filterKeys, this);
7042         }
7043        /* if(this.grow){
7044             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
7045             this.el.on("click", this.autoSize,  this);
7046         }
7047         */
7048         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
7049             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
7050         }
7051         
7052         if (typeof(this.before) == 'object') {
7053             this.before.render(this.el.select('.roo-input-before',true).first());
7054         }
7055         if (typeof(this.after) == 'object') {
7056             this.after.render(this.el.select('.roo-input-after',true).first());
7057         }
7058         
7059         
7060     },
7061     filterValidation : function(e){
7062         if(!e.isNavKeyPress()){
7063             this.validationTask.delay(this.validationDelay);
7064         }
7065     },
7066      /**
7067      * Validates the field value
7068      * @return {Boolean} True if the value is valid, else false
7069      */
7070     validate : function(){
7071         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
7072         if(this.disabled || this.validateValue(this.getRawValue())){
7073             this.clearInvalid();
7074             return true;
7075         }
7076         return false;
7077     },
7078     
7079     
7080     /**
7081      * Validates a value according to the field's validation rules and marks the field as invalid
7082      * if the validation fails
7083      * @param {Mixed} value The value to validate
7084      * @return {Boolean} True if the value is valid, else false
7085      */
7086     validateValue : function(value){
7087         if(value.length < 1)  { // if it's blank
7088              if(this.allowBlank){
7089                 this.clearInvalid();
7090                 return true;
7091              }else{
7092                 this.markInvalid(this.blankText);
7093                 return false;
7094              }
7095         }
7096         if(value.length < this.minLength){
7097             this.markInvalid(String.format(this.minLengthText, this.minLength));
7098             return false;
7099         }
7100         if(value.length > this.maxLength){
7101             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
7102             return false;
7103         }
7104         if(this.vtype){
7105             var vt = Roo.form.VTypes;
7106             if(!vt[this.vtype](value, this)){
7107                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
7108                 return false;
7109             }
7110         }
7111         if(typeof this.validator == "function"){
7112             var msg = this.validator(value);
7113             if(msg !== true){
7114                 this.markInvalid(msg);
7115                 return false;
7116             }
7117         }
7118         if(this.regex && !this.regex.test(value)){
7119             this.markInvalid(this.regexText);
7120             return false;
7121         }
7122         return true;
7123     },
7124
7125     
7126     
7127      // private
7128     fireKey : function(e){
7129         //Roo.log('field ' + e.getKey());
7130         if(e.isNavKeyPress()){
7131             this.fireEvent("specialkey", this, e);
7132         }
7133     },
7134     focus : function (selectText){
7135         if(this.rendered){
7136             this.inputEl().focus();
7137             if(selectText === true){
7138                 this.inputEl().dom.select();
7139             }
7140         }
7141         return this;
7142     } ,
7143     
7144     onFocus : function(){
7145         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7146            // this.el.addClass(this.focusClass);
7147         }
7148         if(!this.hasFocus){
7149             this.hasFocus = true;
7150             this.startValue = this.getValue();
7151             this.fireEvent("focus", this);
7152         }
7153     },
7154     
7155     beforeBlur : Roo.emptyFn,
7156
7157     
7158     // private
7159     onBlur : function(){
7160         this.beforeBlur();
7161         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7162             //this.el.removeClass(this.focusClass);
7163         }
7164         this.hasFocus = false;
7165         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
7166             this.validate();
7167         }
7168         var v = this.getValue();
7169         if(String(v) !== String(this.startValue)){
7170             this.fireEvent('change', this, v, this.startValue);
7171         }
7172         this.fireEvent("blur", this);
7173     },
7174     
7175     /**
7176      * Resets the current field value to the originally loaded value and clears any validation messages
7177      */
7178     reset : function(){
7179         this.setValue(this.originalValue);
7180         this.clearInvalid();
7181     },
7182      /**
7183      * Returns the name of the field
7184      * @return {Mixed} name The name field
7185      */
7186     getName: function(){
7187         return this.name;
7188     },
7189      /**
7190      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
7191      * @return {Mixed} value The field value
7192      */
7193     getValue : function(){
7194         
7195         var v = this.inputEl().getValue();
7196         
7197         return v;
7198     },
7199     /**
7200      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
7201      * @return {Mixed} value The field value
7202      */
7203     getRawValue : function(){
7204         var v = this.inputEl().getValue();
7205         
7206         return v;
7207     },
7208     
7209     /**
7210      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
7211      * @param {Mixed} value The value to set
7212      */
7213     setRawValue : function(v){
7214         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7215     },
7216     
7217     selectText : function(start, end){
7218         var v = this.getRawValue();
7219         if(v.length > 0){
7220             start = start === undefined ? 0 : start;
7221             end = end === undefined ? v.length : end;
7222             var d = this.inputEl().dom;
7223             if(d.setSelectionRange){
7224                 d.setSelectionRange(start, end);
7225             }else if(d.createTextRange){
7226                 var range = d.createTextRange();
7227                 range.moveStart("character", start);
7228                 range.moveEnd("character", v.length-end);
7229                 range.select();
7230             }
7231         }
7232     },
7233     
7234     /**
7235      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
7236      * @param {Mixed} value The value to set
7237      */
7238     setValue : function(v){
7239         this.value = v;
7240         if(this.rendered){
7241             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7242             this.validate();
7243         }
7244     },
7245     
7246     /*
7247     processValue : function(value){
7248         if(this.stripCharsRe){
7249             var newValue = value.replace(this.stripCharsRe, '');
7250             if(newValue !== value){
7251                 this.setRawValue(newValue);
7252                 return newValue;
7253             }
7254         }
7255         return value;
7256     },
7257   */
7258     preFocus : function(){
7259         
7260         if(this.selectOnFocus){
7261             this.inputEl().dom.select();
7262         }
7263     },
7264     filterKeys : function(e){
7265         var k = e.getKey();
7266         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
7267             return;
7268         }
7269         var c = e.getCharCode(), cc = String.fromCharCode(c);
7270         if(Roo.isIE && (e.isSpecialKey() || !cc)){
7271             return;
7272         }
7273         if(!this.maskRe.test(cc)){
7274             e.stopEvent();
7275         }
7276     },
7277      /**
7278      * Clear any invalid styles/messages for this field
7279      */
7280     clearInvalid : function(){
7281         
7282         if(!this.el || this.preventMark){ // not rendered
7283             return;
7284         }
7285         this.el.removeClass(this.invalidClass);
7286         /*
7287         switch(this.msgTarget){
7288             case 'qtip':
7289                 this.el.dom.qtip = '';
7290                 break;
7291             case 'title':
7292                 this.el.dom.title = '';
7293                 break;
7294             case 'under':
7295                 if(this.errorEl){
7296                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
7297                 }
7298                 break;
7299             case 'side':
7300                 if(this.errorIcon){
7301                     this.errorIcon.dom.qtip = '';
7302                     this.errorIcon.hide();
7303                     this.un('resize', this.alignErrorIcon, this);
7304                 }
7305                 break;
7306             default:
7307                 var t = Roo.getDom(this.msgTarget);
7308                 t.innerHTML = '';
7309                 t.style.display = 'none';
7310                 break;
7311         }
7312         */
7313         this.fireEvent('valid', this);
7314     },
7315      /**
7316      * Mark this field as invalid
7317      * @param {String} msg The validation message
7318      */
7319     markInvalid : function(msg){
7320         if(!this.el  || this.preventMark){ // not rendered
7321             return;
7322         }
7323         this.el.addClass(this.invalidClass);
7324         /*
7325         msg = msg || this.invalidText;
7326         switch(this.msgTarget){
7327             case 'qtip':
7328                 this.el.dom.qtip = msg;
7329                 this.el.dom.qclass = 'x-form-invalid-tip';
7330                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
7331                     Roo.QuickTips.enable();
7332                 }
7333                 break;
7334             case 'title':
7335                 this.el.dom.title = msg;
7336                 break;
7337             case 'under':
7338                 if(!this.errorEl){
7339                     var elp = this.el.findParent('.x-form-element', 5, true);
7340                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
7341                     this.errorEl.setWidth(elp.getWidth(true)-20);
7342                 }
7343                 this.errorEl.update(msg);
7344                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
7345                 break;
7346             case 'side':
7347                 if(!this.errorIcon){
7348                     var elp = this.el.findParent('.x-form-element', 5, true);
7349                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
7350                 }
7351                 this.alignErrorIcon();
7352                 this.errorIcon.dom.qtip = msg;
7353                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
7354                 this.errorIcon.show();
7355                 this.on('resize', this.alignErrorIcon, this);
7356                 break;
7357             default:
7358                 var t = Roo.getDom(this.msgTarget);
7359                 t.innerHTML = msg;
7360                 t.style.display = this.msgDisplay;
7361                 break;
7362         }
7363         */
7364         this.fireEvent('invalid', this, msg);
7365     },
7366     // private
7367     SafariOnKeyDown : function(event)
7368     {
7369         // this is a workaround for a password hang bug on chrome/ webkit.
7370         
7371         var isSelectAll = false;
7372         
7373         if(this.inputEl().dom.selectionEnd > 0){
7374             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
7375         }
7376         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
7377             event.preventDefault();
7378             this.setValue('');
7379             return;
7380         }
7381         
7382         if(isSelectAll){ // backspace and delete key
7383             
7384             event.preventDefault();
7385             // this is very hacky as keydown always get's upper case.
7386             //
7387             var cc = String.fromCharCode(event.getCharCode());
7388             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
7389             
7390         }
7391     },
7392     adjustWidth : function(tag, w){
7393         tag = tag.toLowerCase();
7394         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
7395             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
7396                 if(tag == 'input'){
7397                     return w + 2;
7398                 }
7399                 if(tag == 'textarea'){
7400                     return w-2;
7401                 }
7402             }else if(Roo.isOpera){
7403                 if(tag == 'input'){
7404                     return w + 2;
7405                 }
7406                 if(tag == 'textarea'){
7407                     return w-2;
7408                 }
7409             }
7410         }
7411         return w;
7412     }
7413     
7414 });
7415
7416  
7417 /*
7418  * - LGPL
7419  *
7420  * Input
7421  * 
7422  */
7423
7424 /**
7425  * @class Roo.bootstrap.TextArea
7426  * @extends Roo.bootstrap.Input
7427  * Bootstrap TextArea class
7428  * @cfg {Number} cols Specifies the visible width of a text area
7429  * @cfg {Number} rows Specifies the visible number of lines in a text area
7430  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
7431  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
7432  * @cfg {string} html text
7433  * 
7434  * @constructor
7435  * Create a new TextArea
7436  * @param {Object} config The config object
7437  */
7438
7439 Roo.bootstrap.TextArea = function(config){
7440     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
7441    
7442 };
7443
7444 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
7445      
7446     cols : false,
7447     rows : 5,
7448     readOnly : false,
7449     warp : 'soft',
7450     resize : false,
7451     value: false,
7452     html: false,
7453     
7454     getAutoCreate : function(){
7455         
7456         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7457         
7458         var id = Roo.id();
7459         
7460         var cfg = {};
7461         
7462         var input =  {
7463             tag: 'textarea',
7464             id : id,
7465             warp : this.warp,
7466             rows : this.rows,
7467             value : this.value || '',
7468             html: this.html || '',
7469             cls : 'form-control',
7470             placeholder : this.placeholder || '' 
7471             
7472         };
7473         
7474         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7475             input.maxLength = this.maxLength;
7476         }
7477         
7478         if(this.resize){
7479             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
7480         }
7481         
7482         if(this.cols){
7483             input.cols = this.cols;
7484         }
7485         
7486         if (this.readOnly) {
7487             input.readonly = true;
7488         }
7489         
7490         if (this.name) {
7491             input.name = this.name;
7492         }
7493         
7494         if (this.size) {
7495             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
7496         }
7497         
7498         var settings=this;
7499         ['xs','sm','md','lg'].map(function(size){
7500             if (settings[size]) {
7501                 cfg.cls += ' col-' + size + '-' + settings[size];
7502             }
7503         });
7504         
7505         var inputblock = input;
7506         
7507         if (this.before || this.after) {
7508             
7509             inputblock = {
7510                 cls : 'input-group',
7511                 cn :  [] 
7512             };
7513             if (this.before) {
7514                 inputblock.cn.push({
7515                     tag :'span',
7516                     cls : 'input-group-addon',
7517                     html : this.before
7518                 });
7519             }
7520             inputblock.cn.push(input);
7521             if (this.after) {
7522                 inputblock.cn.push({
7523                     tag :'span',
7524                     cls : 'input-group-addon',
7525                     html : this.after
7526                 });
7527             }
7528             
7529         }
7530         
7531         if (align ==='left' && this.fieldLabel.length) {
7532                 Roo.log("left and has label");
7533                 cfg.cn = [
7534                     
7535                     {
7536                         tag: 'label',
7537                         'for' :  id,
7538                         cls : 'control-label col-sm-' + this.labelWidth,
7539                         html : this.fieldLabel
7540                         
7541                     },
7542                     {
7543                         cls : "col-sm-" + (12 - this.labelWidth), 
7544                         cn: [
7545                             inputblock
7546                         ]
7547                     }
7548                     
7549                 ];
7550         } else if ( this.fieldLabel.length) {
7551                 Roo.log(" label");
7552                  cfg.cn = [
7553                    
7554                     {
7555                         tag: 'label',
7556                         //cls : 'input-group-addon',
7557                         html : this.fieldLabel
7558                         
7559                     },
7560                     
7561                     inputblock
7562                     
7563                 ];
7564
7565         } else {
7566             
7567                    Roo.log(" no label && no align");
7568                 cfg.cn = [
7569                     
7570                         inputblock
7571                     
7572                 ];
7573                 
7574                 
7575         }
7576         
7577         if (this.disabled) {
7578             input.disabled=true;
7579         }
7580         
7581         return cfg;
7582         
7583     },
7584     /**
7585      * return the real textarea element.
7586      */
7587     inputEl: function ()
7588     {
7589         return this.el.select('textarea.form-control',true).first();
7590     }
7591 });
7592
7593  
7594 /*
7595  * - LGPL
7596  *
7597  * trigger field - base class for combo..
7598  * 
7599  */
7600  
7601 /**
7602  * @class Roo.bootstrap.TriggerField
7603  * @extends Roo.bootstrap.Input
7604  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
7605  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
7606  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
7607  * for which you can provide a custom implementation.  For example:
7608  * <pre><code>
7609 var trigger = new Roo.bootstrap.TriggerField();
7610 trigger.onTriggerClick = myTriggerFn;
7611 trigger.applyTo('my-field');
7612 </code></pre>
7613  *
7614  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
7615  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
7616  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
7617  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
7618  * @constructor
7619  * Create a new TriggerField.
7620  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
7621  * to the base TextField)
7622  */
7623 Roo.bootstrap.TriggerField = function(config){
7624     this.mimicing = false;
7625     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
7626 };
7627
7628 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
7629     /**
7630      * @cfg {String} triggerClass A CSS class to apply to the trigger
7631      */
7632      /**
7633      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
7634      */
7635     hideTrigger:false,
7636
7637     /** @cfg {Boolean} grow @hide */
7638     /** @cfg {Number} growMin @hide */
7639     /** @cfg {Number} growMax @hide */
7640
7641     /**
7642      * @hide 
7643      * @method
7644      */
7645     autoSize: Roo.emptyFn,
7646     // private
7647     monitorTab : true,
7648     // private
7649     deferHeight : true,
7650
7651     
7652     actionMode : 'wrap',
7653     
7654     
7655     
7656     getAutoCreate : function(){
7657        
7658         var align = this.labelAlign || this.parentLabelAlign();
7659         
7660         var id = Roo.id();
7661         
7662         var cfg = {
7663             cls: 'form-group' //input-group
7664         };
7665         
7666         
7667         var input =  {
7668             tag: 'input',
7669             id : id,
7670             type : this.inputType,
7671             cls : 'form-control',
7672             autocomplete: 'off',
7673             placeholder : this.placeholder || '' 
7674             
7675         };
7676         if (this.name) {
7677             input.name = this.name;
7678         }
7679         if (this.size) {
7680             input.cls += ' input-' + this.size;
7681         }
7682         
7683         if (this.disabled) {
7684             input.disabled=true;
7685         }
7686         
7687         var inputblock = input;
7688         
7689         if (this.before || this.after) {
7690             
7691             inputblock = {
7692                 cls : 'input-group',
7693                 cn :  [] 
7694             };
7695             if (this.before) {
7696                 inputblock.cn.push({
7697                     tag :'span',
7698                     cls : 'input-group-addon',
7699                     html : this.before
7700                 });
7701             }
7702             inputblock.cn.push(input);
7703             if (this.after) {
7704                 inputblock.cn.push({
7705                     tag :'span',
7706                     cls : 'input-group-addon',
7707                     html : this.after
7708                 });
7709             }
7710             
7711         };
7712         
7713         var box = {
7714             tag: 'div',
7715             cn: [
7716                 {
7717                     tag: 'input',
7718                     type : 'hidden',
7719                     cls: 'form-hidden-field'
7720                 },
7721                 inputblock
7722             ]
7723             
7724         };
7725         
7726         if(this.multiple){
7727             Roo.log('multiple');
7728             
7729             box = {
7730                 tag: 'div',
7731                 cn: [
7732                     {
7733                         tag: 'input',
7734                         type : 'hidden',
7735                         cls: 'form-hidden-field'
7736                     },
7737                     {
7738                         tag: 'ul',
7739                         cls: 'select2-choices',
7740                         cn:[
7741                             {
7742                                 tag: 'li',
7743                                 cls: 'select2-search-field',
7744                                 cn: [
7745
7746                                     inputblock
7747                                 ]
7748                             }
7749                         ]
7750                     }
7751                 ]
7752             }
7753         };
7754         
7755         var combobox = {
7756             cls: 'select2-container input-group',
7757             cn: [
7758                 box
7759 //                {
7760 //                    tag: 'ul',
7761 //                    cls: 'typeahead typeahead-long dropdown-menu',
7762 //                    style: 'display:none'
7763 //                }
7764             ]
7765         };
7766         
7767         if(!this.multiple){
7768             combobox.cn.push({
7769                 tag :'span',
7770                 cls : 'input-group-addon btn dropdown-toggle',
7771                 cn : [
7772                     {
7773                         tag: 'span',
7774                         cls: 'caret'
7775                     },
7776                     {
7777                         tag: 'span',
7778                         cls: 'combobox-clear',
7779                         cn  : [
7780                             {
7781                                 tag : 'i',
7782                                 cls: 'icon-remove'
7783                             }
7784                         ]
7785                     }
7786                 ]
7787
7788             })
7789         }
7790         
7791         if(this.multiple){
7792             combobox.cls += ' select2-container-multi';
7793         }
7794         
7795         if (align ==='left' && this.fieldLabel.length) {
7796             
7797                 Roo.log("left and has label");
7798                 cfg.cn = [
7799                     
7800                     {
7801                         tag: 'label',
7802                         'for' :  id,
7803                         cls : 'control-label col-sm-' + this.labelWidth,
7804                         html : this.fieldLabel
7805                         
7806                     },
7807                     {
7808                         cls : "col-sm-" + (12 - this.labelWidth), 
7809                         cn: [
7810                             combobox
7811                         ]
7812                     }
7813                     
7814                 ];
7815         } else if ( this.fieldLabel.length) {
7816                 Roo.log(" label");
7817                  cfg.cn = [
7818                    
7819                     {
7820                         tag: 'label',
7821                         //cls : 'input-group-addon',
7822                         html : this.fieldLabel
7823                         
7824                     },
7825                     
7826                     combobox
7827                     
7828                 ];
7829
7830         } else {
7831             
7832                 Roo.log(" no label && no align");
7833                 cfg = combobox
7834                      
7835                 
7836         }
7837          
7838         var settings=this;
7839         ['xs','sm','md','lg'].map(function(size){
7840             if (settings[size]) {
7841                 cfg.cls += ' col-' + size + '-' + settings[size];
7842             }
7843         });
7844         
7845         return cfg;
7846         
7847     },
7848     
7849     
7850     
7851     // private
7852     onResize : function(w, h){
7853 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
7854 //        if(typeof w == 'number'){
7855 //            var x = w - this.trigger.getWidth();
7856 //            this.inputEl().setWidth(this.adjustWidth('input', x));
7857 //            this.trigger.setStyle('left', x+'px');
7858 //        }
7859     },
7860
7861     // private
7862     adjustSize : Roo.BoxComponent.prototype.adjustSize,
7863
7864     // private
7865     getResizeEl : function(){
7866         return this.inputEl();
7867     },
7868
7869     // private
7870     getPositionEl : function(){
7871         return this.inputEl();
7872     },
7873
7874     // private
7875     alignErrorIcon : function(){
7876         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
7877     },
7878
7879     // private
7880     initEvents : function(){
7881         
7882         this.createList();
7883         
7884         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
7885         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
7886         if(!this.multiple){
7887             this.trigger = this.el.select('span.dropdown-toggle',true).first();
7888             if(this.hideTrigger){
7889                 this.trigger.setDisplayed(false);
7890             }
7891             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
7892         }
7893         
7894         if(this.multiple){
7895             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
7896         }
7897         
7898         //this.trigger.addClassOnOver('x-form-trigger-over');
7899         //this.trigger.addClassOnClick('x-form-trigger-click');
7900         
7901         //if(!this.width){
7902         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
7903         //}
7904     },
7905     
7906     createList : function()
7907     {
7908         this.list = Roo.get(document.body).createChild({
7909             tag: 'ul',
7910             cls: 'typeahead typeahead-long dropdown-menu',
7911             style: 'display:none'
7912         });
7913     },
7914
7915     // private
7916     initTrigger : function(){
7917        
7918     },
7919
7920     // private
7921     onDestroy : function(){
7922         if(this.trigger){
7923             this.trigger.removeAllListeners();
7924           //  this.trigger.remove();
7925         }
7926         //if(this.wrap){
7927         //    this.wrap.remove();
7928         //}
7929         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
7930     },
7931
7932     // private
7933     onFocus : function(){
7934         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
7935         /*
7936         if(!this.mimicing){
7937             this.wrap.addClass('x-trigger-wrap-focus');
7938             this.mimicing = true;
7939             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
7940             if(this.monitorTab){
7941                 this.el.on("keydown", this.checkTab, this);
7942             }
7943         }
7944         */
7945     },
7946
7947     // private
7948     checkTab : function(e){
7949         if(e.getKey() == e.TAB){
7950             this.triggerBlur();
7951         }
7952     },
7953
7954     // private
7955     onBlur : function(){
7956         // do nothing
7957     },
7958
7959     // private
7960     mimicBlur : function(e, t){
7961         /*
7962         if(!this.wrap.contains(t) && this.validateBlur()){
7963             this.triggerBlur();
7964         }
7965         */
7966     },
7967
7968     // private
7969     triggerBlur : function(){
7970         this.mimicing = false;
7971         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
7972         if(this.monitorTab){
7973             this.el.un("keydown", this.checkTab, this);
7974         }
7975         //this.wrap.removeClass('x-trigger-wrap-focus');
7976         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
7977     },
7978
7979     // private
7980     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
7981     validateBlur : function(e, t){
7982         return true;
7983     },
7984
7985     // private
7986     onDisable : function(){
7987         this.inputEl().dom.disabled = true;
7988         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
7989         //if(this.wrap){
7990         //    this.wrap.addClass('x-item-disabled');
7991         //}
7992     },
7993
7994     // private
7995     onEnable : function(){
7996         this.inputEl().dom.disabled = false;
7997         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
7998         //if(this.wrap){
7999         //    this.el.removeClass('x-item-disabled');
8000         //}
8001     },
8002
8003     // private
8004     onShow : function(){
8005         var ae = this.getActionEl();
8006         
8007         if(ae){
8008             ae.dom.style.display = '';
8009             ae.dom.style.visibility = 'visible';
8010         }
8011     },
8012
8013     // private
8014     
8015     onHide : function(){
8016         var ae = this.getActionEl();
8017         ae.dom.style.display = 'none';
8018     },
8019
8020     /**
8021      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
8022      * by an implementing function.
8023      * @method
8024      * @param {EventObject} e
8025      */
8026     onTriggerClick : Roo.emptyFn
8027 });
8028  /*
8029  * Based on:
8030  * Ext JS Library 1.1.1
8031  * Copyright(c) 2006-2007, Ext JS, LLC.
8032  *
8033  * Originally Released Under LGPL - original licence link has changed is not relivant.
8034  *
8035  * Fork - LGPL
8036  * <script type="text/javascript">
8037  */
8038
8039
8040 /**
8041  * @class Roo.data.SortTypes
8042  * @singleton
8043  * Defines the default sorting (casting?) comparison functions used when sorting data.
8044  */
8045 Roo.data.SortTypes = {
8046     /**
8047      * Default sort that does nothing
8048      * @param {Mixed} s The value being converted
8049      * @return {Mixed} The comparison value
8050      */
8051     none : function(s){
8052         return s;
8053     },
8054     
8055     /**
8056      * The regular expression used to strip tags
8057      * @type {RegExp}
8058      * @property
8059      */
8060     stripTagsRE : /<\/?[^>]+>/gi,
8061     
8062     /**
8063      * Strips all HTML tags to sort on text only
8064      * @param {Mixed} s The value being converted
8065      * @return {String} The comparison value
8066      */
8067     asText : function(s){
8068         return String(s).replace(this.stripTagsRE, "");
8069     },
8070     
8071     /**
8072      * Strips all HTML tags to sort on text only - Case insensitive
8073      * @param {Mixed} s The value being converted
8074      * @return {String} The comparison value
8075      */
8076     asUCText : function(s){
8077         return String(s).toUpperCase().replace(this.stripTagsRE, "");
8078     },
8079     
8080     /**
8081      * Case insensitive string
8082      * @param {Mixed} s The value being converted
8083      * @return {String} The comparison value
8084      */
8085     asUCString : function(s) {
8086         return String(s).toUpperCase();
8087     },
8088     
8089     /**
8090      * Date sorting
8091      * @param {Mixed} s The value being converted
8092      * @return {Number} The comparison value
8093      */
8094     asDate : function(s) {
8095         if(!s){
8096             return 0;
8097         }
8098         if(s instanceof Date){
8099             return s.getTime();
8100         }
8101         return Date.parse(String(s));
8102     },
8103     
8104     /**
8105      * Float sorting
8106      * @param {Mixed} s The value being converted
8107      * @return {Float} The comparison value
8108      */
8109     asFloat : function(s) {
8110         var val = parseFloat(String(s).replace(/,/g, ""));
8111         if(isNaN(val)) val = 0;
8112         return val;
8113     },
8114     
8115     /**
8116      * Integer sorting
8117      * @param {Mixed} s The value being converted
8118      * @return {Number} The comparison value
8119      */
8120     asInt : function(s) {
8121         var val = parseInt(String(s).replace(/,/g, ""));
8122         if(isNaN(val)) val = 0;
8123         return val;
8124     }
8125 };/*
8126  * Based on:
8127  * Ext JS Library 1.1.1
8128  * Copyright(c) 2006-2007, Ext JS, LLC.
8129  *
8130  * Originally Released Under LGPL - original licence link has changed is not relivant.
8131  *
8132  * Fork - LGPL
8133  * <script type="text/javascript">
8134  */
8135
8136 /**
8137 * @class Roo.data.Record
8138  * Instances of this class encapsulate both record <em>definition</em> information, and record
8139  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
8140  * to access Records cached in an {@link Roo.data.Store} object.<br>
8141  * <p>
8142  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
8143  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
8144  * objects.<br>
8145  * <p>
8146  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
8147  * @constructor
8148  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
8149  * {@link #create}. The parameters are the same.
8150  * @param {Array} data An associative Array of data values keyed by the field name.
8151  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
8152  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
8153  * not specified an integer id is generated.
8154  */
8155 Roo.data.Record = function(data, id){
8156     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
8157     this.data = data;
8158 };
8159
8160 /**
8161  * Generate a constructor for a specific record layout.
8162  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
8163  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
8164  * Each field definition object may contain the following properties: <ul>
8165  * <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,
8166  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
8167  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
8168  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
8169  * is being used, then this is a string containing the javascript expression to reference the data relative to 
8170  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
8171  * to the data item relative to the record element. If the mapping expression is the same as the field name,
8172  * this may be omitted.</p></li>
8173  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
8174  * <ul><li>auto (Default, implies no conversion)</li>
8175  * <li>string</li>
8176  * <li>int</li>
8177  * <li>float</li>
8178  * <li>boolean</li>
8179  * <li>date</li></ul></p></li>
8180  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
8181  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
8182  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
8183  * by the Reader into an object that will be stored in the Record. It is passed the
8184  * following parameters:<ul>
8185  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
8186  * </ul></p></li>
8187  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
8188  * </ul>
8189  * <br>usage:<br><pre><code>
8190 var TopicRecord = Roo.data.Record.create(
8191     {name: 'title', mapping: 'topic_title'},
8192     {name: 'author', mapping: 'username'},
8193     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
8194     {name: 'lastPost', mapping: 'post_time', type: 'date'},
8195     {name: 'lastPoster', mapping: 'user2'},
8196     {name: 'excerpt', mapping: 'post_text'}
8197 );
8198
8199 var myNewRecord = new TopicRecord({
8200     title: 'Do my job please',
8201     author: 'noobie',
8202     totalPosts: 1,
8203     lastPost: new Date(),
8204     lastPoster: 'Animal',
8205     excerpt: 'No way dude!'
8206 });
8207 myStore.add(myNewRecord);
8208 </code></pre>
8209  * @method create
8210  * @static
8211  */
8212 Roo.data.Record.create = function(o){
8213     var f = function(){
8214         f.superclass.constructor.apply(this, arguments);
8215     };
8216     Roo.extend(f, Roo.data.Record);
8217     var p = f.prototype;
8218     p.fields = new Roo.util.MixedCollection(false, function(field){
8219         return field.name;
8220     });
8221     for(var i = 0, len = o.length; i < len; i++){
8222         p.fields.add(new Roo.data.Field(o[i]));
8223     }
8224     f.getField = function(name){
8225         return p.fields.get(name);  
8226     };
8227     return f;
8228 };
8229
8230 Roo.data.Record.AUTO_ID = 1000;
8231 Roo.data.Record.EDIT = 'edit';
8232 Roo.data.Record.REJECT = 'reject';
8233 Roo.data.Record.COMMIT = 'commit';
8234
8235 Roo.data.Record.prototype = {
8236     /**
8237      * Readonly flag - true if this record has been modified.
8238      * @type Boolean
8239      */
8240     dirty : false,
8241     editing : false,
8242     error: null,
8243     modified: null,
8244
8245     // private
8246     join : function(store){
8247         this.store = store;
8248     },
8249
8250     /**
8251      * Set the named field to the specified value.
8252      * @param {String} name The name of the field to set.
8253      * @param {Object} value The value to set the field to.
8254      */
8255     set : function(name, value){
8256         if(this.data[name] == value){
8257             return;
8258         }
8259         this.dirty = true;
8260         if(!this.modified){
8261             this.modified = {};
8262         }
8263         if(typeof this.modified[name] == 'undefined'){
8264             this.modified[name] = this.data[name];
8265         }
8266         this.data[name] = value;
8267         if(!this.editing && this.store){
8268             this.store.afterEdit(this);
8269         }       
8270     },
8271
8272     /**
8273      * Get the value of the named field.
8274      * @param {String} name The name of the field to get the value of.
8275      * @return {Object} The value of the field.
8276      */
8277     get : function(name){
8278         return this.data[name]; 
8279     },
8280
8281     // private
8282     beginEdit : function(){
8283         this.editing = true;
8284         this.modified = {}; 
8285     },
8286
8287     // private
8288     cancelEdit : function(){
8289         this.editing = false;
8290         delete this.modified;
8291     },
8292
8293     // private
8294     endEdit : function(){
8295         this.editing = false;
8296         if(this.dirty && this.store){
8297             this.store.afterEdit(this);
8298         }
8299     },
8300
8301     /**
8302      * Usually called by the {@link Roo.data.Store} which owns the Record.
8303      * Rejects all changes made to the Record since either creation, or the last commit operation.
8304      * Modified fields are reverted to their original values.
8305      * <p>
8306      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8307      * of reject operations.
8308      */
8309     reject : function(){
8310         var m = this.modified;
8311         for(var n in m){
8312             if(typeof m[n] != "function"){
8313                 this.data[n] = m[n];
8314             }
8315         }
8316         this.dirty = false;
8317         delete this.modified;
8318         this.editing = false;
8319         if(this.store){
8320             this.store.afterReject(this);
8321         }
8322     },
8323
8324     /**
8325      * Usually called by the {@link Roo.data.Store} which owns the Record.
8326      * Commits all changes made to the Record since either creation, or the last commit operation.
8327      * <p>
8328      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8329      * of commit operations.
8330      */
8331     commit : function(){
8332         this.dirty = false;
8333         delete this.modified;
8334         this.editing = false;
8335         if(this.store){
8336             this.store.afterCommit(this);
8337         }
8338     },
8339
8340     // private
8341     hasError : function(){
8342         return this.error != null;
8343     },
8344
8345     // private
8346     clearError : function(){
8347         this.error = null;
8348     },
8349
8350     /**
8351      * Creates a copy of this record.
8352      * @param {String} id (optional) A new record id if you don't want to use this record's id
8353      * @return {Record}
8354      */
8355     copy : function(newId) {
8356         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
8357     }
8358 };/*
8359  * Based on:
8360  * Ext JS Library 1.1.1
8361  * Copyright(c) 2006-2007, Ext JS, LLC.
8362  *
8363  * Originally Released Under LGPL - original licence link has changed is not relivant.
8364  *
8365  * Fork - LGPL
8366  * <script type="text/javascript">
8367  */
8368
8369
8370
8371 /**
8372  * @class Roo.data.Store
8373  * @extends Roo.util.Observable
8374  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
8375  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
8376  * <p>
8377  * 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
8378  * has no knowledge of the format of the data returned by the Proxy.<br>
8379  * <p>
8380  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
8381  * instances from the data object. These records are cached and made available through accessor functions.
8382  * @constructor
8383  * Creates a new Store.
8384  * @param {Object} config A config object containing the objects needed for the Store to access data,
8385  * and read the data into Records.
8386  */
8387 Roo.data.Store = function(config){
8388     this.data = new Roo.util.MixedCollection(false);
8389     this.data.getKey = function(o){
8390         return o.id;
8391     };
8392     this.baseParams = {};
8393     // private
8394     this.paramNames = {
8395         "start" : "start",
8396         "limit" : "limit",
8397         "sort" : "sort",
8398         "dir" : "dir",
8399         "multisort" : "_multisort"
8400     };
8401
8402     if(config && config.data){
8403         this.inlineData = config.data;
8404         delete config.data;
8405     }
8406
8407     Roo.apply(this, config);
8408     
8409     if(this.reader){ // reader passed
8410         this.reader = Roo.factory(this.reader, Roo.data);
8411         this.reader.xmodule = this.xmodule || false;
8412         if(!this.recordType){
8413             this.recordType = this.reader.recordType;
8414         }
8415         if(this.reader.onMetaChange){
8416             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
8417         }
8418     }
8419
8420     if(this.recordType){
8421         this.fields = this.recordType.prototype.fields;
8422     }
8423     this.modified = [];
8424
8425     this.addEvents({
8426         /**
8427          * @event datachanged
8428          * Fires when the data cache has changed, and a widget which is using this Store
8429          * as a Record cache should refresh its view.
8430          * @param {Store} this
8431          */
8432         datachanged : true,
8433         /**
8434          * @event metachange
8435          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
8436          * @param {Store} this
8437          * @param {Object} meta The JSON metadata
8438          */
8439         metachange : true,
8440         /**
8441          * @event add
8442          * Fires when Records have been added to the Store
8443          * @param {Store} this
8444          * @param {Roo.data.Record[]} records The array of Records added
8445          * @param {Number} index The index at which the record(s) were added
8446          */
8447         add : true,
8448         /**
8449          * @event remove
8450          * Fires when a Record has been removed from the Store
8451          * @param {Store} this
8452          * @param {Roo.data.Record} record The Record that was removed
8453          * @param {Number} index The index at which the record was removed
8454          */
8455         remove : true,
8456         /**
8457          * @event update
8458          * Fires when a Record has been updated
8459          * @param {Store} this
8460          * @param {Roo.data.Record} record The Record that was updated
8461          * @param {String} operation The update operation being performed.  Value may be one of:
8462          * <pre><code>
8463  Roo.data.Record.EDIT
8464  Roo.data.Record.REJECT
8465  Roo.data.Record.COMMIT
8466          * </code></pre>
8467          */
8468         update : true,
8469         /**
8470          * @event clear
8471          * Fires when the data cache has been cleared.
8472          * @param {Store} this
8473          */
8474         clear : true,
8475         /**
8476          * @event beforeload
8477          * Fires before a request is made for a new data object.  If the beforeload handler returns false
8478          * the load action will be canceled.
8479          * @param {Store} this
8480          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8481          */
8482         beforeload : true,
8483         /**
8484          * @event beforeloadadd
8485          * Fires after a new set of Records has been loaded.
8486          * @param {Store} this
8487          * @param {Roo.data.Record[]} records The Records that were loaded
8488          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8489          */
8490         beforeloadadd : true,
8491         /**
8492          * @event load
8493          * Fires after a new set of Records has been loaded, before they are added to the store.
8494          * @param {Store} this
8495          * @param {Roo.data.Record[]} records The Records that were loaded
8496          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8497          * @params {Object} return from reader
8498          */
8499         load : true,
8500         /**
8501          * @event loadexception
8502          * Fires if an exception occurs in the Proxy during loading.
8503          * Called with the signature of the Proxy's "loadexception" event.
8504          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
8505          * 
8506          * @param {Proxy} 
8507          * @param {Object} return from JsonData.reader() - success, totalRecords, records
8508          * @param {Object} load options 
8509          * @param {Object} jsonData from your request (normally this contains the Exception)
8510          */
8511         loadexception : true
8512     });
8513     
8514     if(this.proxy){
8515         this.proxy = Roo.factory(this.proxy, Roo.data);
8516         this.proxy.xmodule = this.xmodule || false;
8517         this.relayEvents(this.proxy,  ["loadexception"]);
8518     }
8519     this.sortToggle = {};
8520     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
8521
8522     Roo.data.Store.superclass.constructor.call(this);
8523
8524     if(this.inlineData){
8525         this.loadData(this.inlineData);
8526         delete this.inlineData;
8527     }
8528 };
8529
8530 Roo.extend(Roo.data.Store, Roo.util.Observable, {
8531      /**
8532     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
8533     * without a remote query - used by combo/forms at present.
8534     */
8535     
8536     /**
8537     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
8538     */
8539     /**
8540     * @cfg {Array} data Inline data to be loaded when the store is initialized.
8541     */
8542     /**
8543     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
8544     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
8545     */
8546     /**
8547     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
8548     * on any HTTP request
8549     */
8550     /**
8551     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
8552     */
8553     /**
8554     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
8555     */
8556     multiSort: false,
8557     /**
8558     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
8559     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
8560     */
8561     remoteSort : false,
8562
8563     /**
8564     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
8565      * loaded or when a record is removed. (defaults to false).
8566     */
8567     pruneModifiedRecords : false,
8568
8569     // private
8570     lastOptions : null,
8571
8572     /**
8573      * Add Records to the Store and fires the add event.
8574      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8575      */
8576     add : function(records){
8577         records = [].concat(records);
8578         for(var i = 0, len = records.length; i < len; i++){
8579             records[i].join(this);
8580         }
8581         var index = this.data.length;
8582         this.data.addAll(records);
8583         this.fireEvent("add", this, records, index);
8584     },
8585
8586     /**
8587      * Remove a Record from the Store and fires the remove event.
8588      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
8589      */
8590     remove : function(record){
8591         var index = this.data.indexOf(record);
8592         this.data.removeAt(index);
8593         if(this.pruneModifiedRecords){
8594             this.modified.remove(record);
8595         }
8596         this.fireEvent("remove", this, record, index);
8597     },
8598
8599     /**
8600      * Remove all Records from the Store and fires the clear event.
8601      */
8602     removeAll : function(){
8603         this.data.clear();
8604         if(this.pruneModifiedRecords){
8605             this.modified = [];
8606         }
8607         this.fireEvent("clear", this);
8608     },
8609
8610     /**
8611      * Inserts Records to the Store at the given index and fires the add event.
8612      * @param {Number} index The start index at which to insert the passed Records.
8613      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8614      */
8615     insert : function(index, records){
8616         records = [].concat(records);
8617         for(var i = 0, len = records.length; i < len; i++){
8618             this.data.insert(index, records[i]);
8619             records[i].join(this);
8620         }
8621         this.fireEvent("add", this, records, index);
8622     },
8623
8624     /**
8625      * Get the index within the cache of the passed Record.
8626      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
8627      * @return {Number} The index of the passed Record. Returns -1 if not found.
8628      */
8629     indexOf : function(record){
8630         return this.data.indexOf(record);
8631     },
8632
8633     /**
8634      * Get the index within the cache of the Record with the passed id.
8635      * @param {String} id The id of the Record to find.
8636      * @return {Number} The index of the Record. Returns -1 if not found.
8637      */
8638     indexOfId : function(id){
8639         return this.data.indexOfKey(id);
8640     },
8641
8642     /**
8643      * Get the Record with the specified id.
8644      * @param {String} id The id of the Record to find.
8645      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
8646      */
8647     getById : function(id){
8648         return this.data.key(id);
8649     },
8650
8651     /**
8652      * Get the Record at the specified index.
8653      * @param {Number} index The index of the Record to find.
8654      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
8655      */
8656     getAt : function(index){
8657         return this.data.itemAt(index);
8658     },
8659
8660     /**
8661      * Returns a range of Records between specified indices.
8662      * @param {Number} startIndex (optional) The starting index (defaults to 0)
8663      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
8664      * @return {Roo.data.Record[]} An array of Records
8665      */
8666     getRange : function(start, end){
8667         return this.data.getRange(start, end);
8668     },
8669
8670     // private
8671     storeOptions : function(o){
8672         o = Roo.apply({}, o);
8673         delete o.callback;
8674         delete o.scope;
8675         this.lastOptions = o;
8676     },
8677
8678     /**
8679      * Loads the Record cache from the configured Proxy using the configured Reader.
8680      * <p>
8681      * If using remote paging, then the first load call must specify the <em>start</em>
8682      * and <em>limit</em> properties in the options.params property to establish the initial
8683      * position within the dataset, and the number of Records to cache on each read from the Proxy.
8684      * <p>
8685      * <strong>It is important to note that for remote data sources, loading is asynchronous,
8686      * and this call will return before the new data has been loaded. Perform any post-processing
8687      * in a callback function, or in a "load" event handler.</strong>
8688      * <p>
8689      * @param {Object} options An object containing properties which control loading options:<ul>
8690      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
8691      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
8692      * passed the following arguments:<ul>
8693      * <li>r : Roo.data.Record[]</li>
8694      * <li>options: Options object from the load call</li>
8695      * <li>success: Boolean success indicator</li></ul></li>
8696      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
8697      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
8698      * </ul>
8699      */
8700     load : function(options){
8701         options = options || {};
8702         if(this.fireEvent("beforeload", this, options) !== false){
8703             this.storeOptions(options);
8704             var p = Roo.apply(options.params || {}, this.baseParams);
8705             // if meta was not loaded from remote source.. try requesting it.
8706             if (!this.reader.metaFromRemote) {
8707                 p._requestMeta = 1;
8708             }
8709             if(this.sortInfo && this.remoteSort){
8710                 var pn = this.paramNames;
8711                 p[pn["sort"]] = this.sortInfo.field;
8712                 p[pn["dir"]] = this.sortInfo.direction;
8713             }
8714             if (this.multiSort) {
8715                 var pn = this.paramNames;
8716                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
8717             }
8718             
8719             this.proxy.load(p, this.reader, this.loadRecords, this, options);
8720         }
8721     },
8722
8723     /**
8724      * Reloads the Record cache from the configured Proxy using the configured Reader and
8725      * the options from the last load operation performed.
8726      * @param {Object} options (optional) An object containing properties which may override the options
8727      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
8728      * the most recently used options are reused).
8729      */
8730     reload : function(options){
8731         this.load(Roo.applyIf(options||{}, this.lastOptions));
8732     },
8733
8734     // private
8735     // Called as a callback by the Reader during a load operation.
8736     loadRecords : function(o, options, success){
8737         if(!o || success === false){
8738             if(success !== false){
8739                 this.fireEvent("load", this, [], options, o);
8740             }
8741             if(options.callback){
8742                 options.callback.call(options.scope || this, [], options, false);
8743             }
8744             return;
8745         }
8746         // if data returned failure - throw an exception.
8747         if (o.success === false) {
8748             // show a message if no listener is registered.
8749             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
8750                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
8751             }
8752             // loadmask wil be hooked into this..
8753             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
8754             return;
8755         }
8756         var r = o.records, t = o.totalRecords || r.length;
8757         
8758         this.fireEvent("beforeloadadd", this, r, options, o);
8759         
8760         if(!options || options.add !== true){
8761             if(this.pruneModifiedRecords){
8762                 this.modified = [];
8763             }
8764             for(var i = 0, len = r.length; i < len; i++){
8765                 r[i].join(this);
8766             }
8767             if(this.snapshot){
8768                 this.data = this.snapshot;
8769                 delete this.snapshot;
8770             }
8771             this.data.clear();
8772             this.data.addAll(r);
8773             this.totalLength = t;
8774             this.applySort();
8775             this.fireEvent("datachanged", this);
8776         }else{
8777             this.totalLength = Math.max(t, this.data.length+r.length);
8778             this.add(r);
8779         }
8780         this.fireEvent("load", this, r, options, o);
8781         if(options.callback){
8782             options.callback.call(options.scope || this, r, options, true);
8783         }
8784     },
8785
8786
8787     /**
8788      * Loads data from a passed data block. A Reader which understands the format of the data
8789      * must have been configured in the constructor.
8790      * @param {Object} data The data block from which to read the Records.  The format of the data expected
8791      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
8792      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
8793      */
8794     loadData : function(o, append){
8795         var r = this.reader.readRecords(o);
8796         this.loadRecords(r, {add: append}, true);
8797     },
8798
8799     /**
8800      * Gets the number of cached records.
8801      * <p>
8802      * <em>If using paging, this may not be the total size of the dataset. If the data object
8803      * used by the Reader contains the dataset size, then the getTotalCount() function returns
8804      * the data set size</em>
8805      */
8806     getCount : function(){
8807         return this.data.length || 0;
8808     },
8809
8810     /**
8811      * Gets the total number of records in the dataset as returned by the server.
8812      * <p>
8813      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
8814      * the dataset size</em>
8815      */
8816     getTotalCount : function(){
8817         return this.totalLength || 0;
8818     },
8819
8820     /**
8821      * Returns the sort state of the Store as an object with two properties:
8822      * <pre><code>
8823  field {String} The name of the field by which the Records are sorted
8824  direction {String} The sort order, "ASC" or "DESC"
8825      * </code></pre>
8826      */
8827     getSortState : function(){
8828         return this.sortInfo;
8829     },
8830
8831     // private
8832     applySort : function(){
8833         if(this.sortInfo && !this.remoteSort){
8834             var s = this.sortInfo, f = s.field;
8835             var st = this.fields.get(f).sortType;
8836             var fn = function(r1, r2){
8837                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
8838                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
8839             };
8840             this.data.sort(s.direction, fn);
8841             if(this.snapshot && this.snapshot != this.data){
8842                 this.snapshot.sort(s.direction, fn);
8843             }
8844         }
8845     },
8846
8847     /**
8848      * Sets the default sort column and order to be used by the next load operation.
8849      * @param {String} fieldName The name of the field to sort by.
8850      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
8851      */
8852     setDefaultSort : function(field, dir){
8853         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
8854     },
8855
8856     /**
8857      * Sort the Records.
8858      * If remote sorting is used, the sort is performed on the server, and the cache is
8859      * reloaded. If local sorting is used, the cache is sorted internally.
8860      * @param {String} fieldName The name of the field to sort by.
8861      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
8862      */
8863     sort : function(fieldName, dir){
8864         var f = this.fields.get(fieldName);
8865         if(!dir){
8866             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
8867             
8868             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
8869                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
8870             }else{
8871                 dir = f.sortDir;
8872             }
8873         }
8874         this.sortToggle[f.name] = dir;
8875         this.sortInfo = {field: f.name, direction: dir};
8876         if(!this.remoteSort){
8877             this.applySort();
8878             this.fireEvent("datachanged", this);
8879         }else{
8880             this.load(this.lastOptions);
8881         }
8882     },
8883
8884     /**
8885      * Calls the specified function for each of the Records in the cache.
8886      * @param {Function} fn The function to call. The Record is passed as the first parameter.
8887      * Returning <em>false</em> aborts and exits the iteration.
8888      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
8889      */
8890     each : function(fn, scope){
8891         this.data.each(fn, scope);
8892     },
8893
8894     /**
8895      * Gets all records modified since the last commit.  Modified records are persisted across load operations
8896      * (e.g., during paging).
8897      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
8898      */
8899     getModifiedRecords : function(){
8900         return this.modified;
8901     },
8902
8903     // private
8904     createFilterFn : function(property, value, anyMatch){
8905         if(!value.exec){ // not a regex
8906             value = String(value);
8907             if(value.length == 0){
8908                 return false;
8909             }
8910             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
8911         }
8912         return function(r){
8913             return value.test(r.data[property]);
8914         };
8915     },
8916
8917     /**
8918      * Sums the value of <i>property</i> for each record between start and end and returns the result.
8919      * @param {String} property A field on your records
8920      * @param {Number} start The record index to start at (defaults to 0)
8921      * @param {Number} end The last record index to include (defaults to length - 1)
8922      * @return {Number} The sum
8923      */
8924     sum : function(property, start, end){
8925         var rs = this.data.items, v = 0;
8926         start = start || 0;
8927         end = (end || end === 0) ? end : rs.length-1;
8928
8929         for(var i = start; i <= end; i++){
8930             v += (rs[i].data[property] || 0);
8931         }
8932         return v;
8933     },
8934
8935     /**
8936      * Filter the records by a specified property.
8937      * @param {String} field A field on your records
8938      * @param {String/RegExp} value Either a string that the field
8939      * should start with or a RegExp to test against the field
8940      * @param {Boolean} anyMatch True to match any part not just the beginning
8941      */
8942     filter : function(property, value, anyMatch){
8943         var fn = this.createFilterFn(property, value, anyMatch);
8944         return fn ? this.filterBy(fn) : this.clearFilter();
8945     },
8946
8947     /**
8948      * Filter by a function. The specified function will be called with each
8949      * record in this data source. If the function returns true the record is included,
8950      * otherwise it is filtered.
8951      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
8952      * @param {Object} scope (optional) The scope of the function (defaults to this)
8953      */
8954     filterBy : function(fn, scope){
8955         this.snapshot = this.snapshot || this.data;
8956         this.data = this.queryBy(fn, scope||this);
8957         this.fireEvent("datachanged", this);
8958     },
8959
8960     /**
8961      * Query the records by a specified property.
8962      * @param {String} field A field on your records
8963      * @param {String/RegExp} value Either a string that the field
8964      * should start with or a RegExp to test against the field
8965      * @param {Boolean} anyMatch True to match any part not just the beginning
8966      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
8967      */
8968     query : function(property, value, anyMatch){
8969         var fn = this.createFilterFn(property, value, anyMatch);
8970         return fn ? this.queryBy(fn) : this.data.clone();
8971     },
8972
8973     /**
8974      * Query by a function. The specified function will be called with each
8975      * record in this data source. If the function returns true the record is included
8976      * in the results.
8977      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
8978      * @param {Object} scope (optional) The scope of the function (defaults to this)
8979       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
8980      **/
8981     queryBy : function(fn, scope){
8982         var data = this.snapshot || this.data;
8983         return data.filterBy(fn, scope||this);
8984     },
8985
8986     /**
8987      * Collects unique values for a particular dataIndex from this store.
8988      * @param {String} dataIndex The property to collect
8989      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
8990      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
8991      * @return {Array} An array of the unique values
8992      **/
8993     collect : function(dataIndex, allowNull, bypassFilter){
8994         var d = (bypassFilter === true && this.snapshot) ?
8995                 this.snapshot.items : this.data.items;
8996         var v, sv, r = [], l = {};
8997         for(var i = 0, len = d.length; i < len; i++){
8998             v = d[i].data[dataIndex];
8999             sv = String(v);
9000             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
9001                 l[sv] = true;
9002                 r[r.length] = v;
9003             }
9004         }
9005         return r;
9006     },
9007
9008     /**
9009      * Revert to a view of the Record cache with no filtering applied.
9010      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
9011      */
9012     clearFilter : function(suppressEvent){
9013         if(this.snapshot && this.snapshot != this.data){
9014             this.data = this.snapshot;
9015             delete this.snapshot;
9016             if(suppressEvent !== true){
9017                 this.fireEvent("datachanged", this);
9018             }
9019         }
9020     },
9021
9022     // private
9023     afterEdit : function(record){
9024         if(this.modified.indexOf(record) == -1){
9025             this.modified.push(record);
9026         }
9027         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
9028     },
9029     
9030     // private
9031     afterReject : function(record){
9032         this.modified.remove(record);
9033         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
9034     },
9035
9036     // private
9037     afterCommit : function(record){
9038         this.modified.remove(record);
9039         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
9040     },
9041
9042     /**
9043      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
9044      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
9045      */
9046     commitChanges : function(){
9047         var m = this.modified.slice(0);
9048         this.modified = [];
9049         for(var i = 0, len = m.length; i < len; i++){
9050             m[i].commit();
9051         }
9052     },
9053
9054     /**
9055      * Cancel outstanding changes on all changed records.
9056      */
9057     rejectChanges : function(){
9058         var m = this.modified.slice(0);
9059         this.modified = [];
9060         for(var i = 0, len = m.length; i < len; i++){
9061             m[i].reject();
9062         }
9063     },
9064
9065     onMetaChange : function(meta, rtype, o){
9066         this.recordType = rtype;
9067         this.fields = rtype.prototype.fields;
9068         delete this.snapshot;
9069         this.sortInfo = meta.sortInfo || this.sortInfo;
9070         this.modified = [];
9071         this.fireEvent('metachange', this, this.reader.meta);
9072     },
9073     
9074     moveIndex : function(data, type)
9075     {
9076         var index = this.indexOf(data);
9077         
9078         var newIndex = index + type;
9079         
9080         this.remove(data);
9081         
9082         this.insert(newIndex, data);
9083         
9084     }
9085 });/*
9086  * Based on:
9087  * Ext JS Library 1.1.1
9088  * Copyright(c) 2006-2007, Ext JS, LLC.
9089  *
9090  * Originally Released Under LGPL - original licence link has changed is not relivant.
9091  *
9092  * Fork - LGPL
9093  * <script type="text/javascript">
9094  */
9095
9096 /**
9097  * @class Roo.data.SimpleStore
9098  * @extends Roo.data.Store
9099  * Small helper class to make creating Stores from Array data easier.
9100  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
9101  * @cfg {Array} fields An array of field definition objects, or field name strings.
9102  * @cfg {Array} data The multi-dimensional array of data
9103  * @constructor
9104  * @param {Object} config
9105  */
9106 Roo.data.SimpleStore = function(config){
9107     Roo.data.SimpleStore.superclass.constructor.call(this, {
9108         isLocal : true,
9109         reader: new Roo.data.ArrayReader({
9110                 id: config.id
9111             },
9112             Roo.data.Record.create(config.fields)
9113         ),
9114         proxy : new Roo.data.MemoryProxy(config.data)
9115     });
9116     this.load();
9117 };
9118 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
9119  * Based on:
9120  * Ext JS Library 1.1.1
9121  * Copyright(c) 2006-2007, Ext JS, LLC.
9122  *
9123  * Originally Released Under LGPL - original licence link has changed is not relivant.
9124  *
9125  * Fork - LGPL
9126  * <script type="text/javascript">
9127  */
9128
9129 /**
9130 /**
9131  * @extends Roo.data.Store
9132  * @class Roo.data.JsonStore
9133  * Small helper class to make creating Stores for JSON data easier. <br/>
9134 <pre><code>
9135 var store = new Roo.data.JsonStore({
9136     url: 'get-images.php',
9137     root: 'images',
9138     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
9139 });
9140 </code></pre>
9141  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
9142  * JsonReader and HttpProxy (unless inline data is provided).</b>
9143  * @cfg {Array} fields An array of field definition objects, or field name strings.
9144  * @constructor
9145  * @param {Object} config
9146  */
9147 Roo.data.JsonStore = function(c){
9148     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
9149         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
9150         reader: new Roo.data.JsonReader(c, c.fields)
9151     }));
9152 };
9153 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
9154  * Based on:
9155  * Ext JS Library 1.1.1
9156  * Copyright(c) 2006-2007, Ext JS, LLC.
9157  *
9158  * Originally Released Under LGPL - original licence link has changed is not relivant.
9159  *
9160  * Fork - LGPL
9161  * <script type="text/javascript">
9162  */
9163
9164  
9165 Roo.data.Field = function(config){
9166     if(typeof config == "string"){
9167         config = {name: config};
9168     }
9169     Roo.apply(this, config);
9170     
9171     if(!this.type){
9172         this.type = "auto";
9173     }
9174     
9175     var st = Roo.data.SortTypes;
9176     // named sortTypes are supported, here we look them up
9177     if(typeof this.sortType == "string"){
9178         this.sortType = st[this.sortType];
9179     }
9180     
9181     // set default sortType for strings and dates
9182     if(!this.sortType){
9183         switch(this.type){
9184             case "string":
9185                 this.sortType = st.asUCString;
9186                 break;
9187             case "date":
9188                 this.sortType = st.asDate;
9189                 break;
9190             default:
9191                 this.sortType = st.none;
9192         }
9193     }
9194
9195     // define once
9196     var stripRe = /[\$,%]/g;
9197
9198     // prebuilt conversion function for this field, instead of
9199     // switching every time we're reading a value
9200     if(!this.convert){
9201         var cv, dateFormat = this.dateFormat;
9202         switch(this.type){
9203             case "":
9204             case "auto":
9205             case undefined:
9206                 cv = function(v){ return v; };
9207                 break;
9208             case "string":
9209                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
9210                 break;
9211             case "int":
9212                 cv = function(v){
9213                     return v !== undefined && v !== null && v !== '' ?
9214                            parseInt(String(v).replace(stripRe, ""), 10) : '';
9215                     };
9216                 break;
9217             case "float":
9218                 cv = function(v){
9219                     return v !== undefined && v !== null && v !== '' ?
9220                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
9221                     };
9222                 break;
9223             case "bool":
9224             case "boolean":
9225                 cv = function(v){ return v === true || v === "true" || v == 1; };
9226                 break;
9227             case "date":
9228                 cv = function(v){
9229                     if(!v){
9230                         return '';
9231                     }
9232                     if(v instanceof Date){
9233                         return v;
9234                     }
9235                     if(dateFormat){
9236                         if(dateFormat == "timestamp"){
9237                             return new Date(v*1000);
9238                         }
9239                         return Date.parseDate(v, dateFormat);
9240                     }
9241                     var parsed = Date.parse(v);
9242                     return parsed ? new Date(parsed) : null;
9243                 };
9244              break;
9245             
9246         }
9247         this.convert = cv;
9248     }
9249 };
9250
9251 Roo.data.Field.prototype = {
9252     dateFormat: null,
9253     defaultValue: "",
9254     mapping: null,
9255     sortType : null,
9256     sortDir : "ASC"
9257 };/*
9258  * Based on:
9259  * Ext JS Library 1.1.1
9260  * Copyright(c) 2006-2007, Ext JS, LLC.
9261  *
9262  * Originally Released Under LGPL - original licence link has changed is not relivant.
9263  *
9264  * Fork - LGPL
9265  * <script type="text/javascript">
9266  */
9267  
9268 // Base class for reading structured data from a data source.  This class is intended to be
9269 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
9270
9271 /**
9272  * @class Roo.data.DataReader
9273  * Base class for reading structured data from a data source.  This class is intended to be
9274  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
9275  */
9276
9277 Roo.data.DataReader = function(meta, recordType){
9278     
9279     this.meta = meta;
9280     
9281     this.recordType = recordType instanceof Array ? 
9282         Roo.data.Record.create(recordType) : recordType;
9283 };
9284
9285 Roo.data.DataReader.prototype = {
9286      /**
9287      * Create an empty record
9288      * @param {Object} data (optional) - overlay some values
9289      * @return {Roo.data.Record} record created.
9290      */
9291     newRow :  function(d) {
9292         var da =  {};
9293         this.recordType.prototype.fields.each(function(c) {
9294             switch( c.type) {
9295                 case 'int' : da[c.name] = 0; break;
9296                 case 'date' : da[c.name] = new Date(); break;
9297                 case 'float' : da[c.name] = 0.0; break;
9298                 case 'boolean' : da[c.name] = false; break;
9299                 default : da[c.name] = ""; break;
9300             }
9301             
9302         });
9303         return new this.recordType(Roo.apply(da, d));
9304     }
9305     
9306 };/*
9307  * Based on:
9308  * Ext JS Library 1.1.1
9309  * Copyright(c) 2006-2007, Ext JS, LLC.
9310  *
9311  * Originally Released Under LGPL - original licence link has changed is not relivant.
9312  *
9313  * Fork - LGPL
9314  * <script type="text/javascript">
9315  */
9316
9317 /**
9318  * @class Roo.data.DataProxy
9319  * @extends Roo.data.Observable
9320  * This class is an abstract base class for implementations which provide retrieval of
9321  * unformatted data objects.<br>
9322  * <p>
9323  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
9324  * (of the appropriate type which knows how to parse the data object) to provide a block of
9325  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
9326  * <p>
9327  * Custom implementations must implement the load method as described in
9328  * {@link Roo.data.HttpProxy#load}.
9329  */
9330 Roo.data.DataProxy = function(){
9331     this.addEvents({
9332         /**
9333          * @event beforeload
9334          * Fires before a network request is made to retrieve a data object.
9335          * @param {Object} This DataProxy object.
9336          * @param {Object} params The params parameter to the load function.
9337          */
9338         beforeload : true,
9339         /**
9340          * @event load
9341          * Fires before the load method's callback is called.
9342          * @param {Object} This DataProxy object.
9343          * @param {Object} o The data object.
9344          * @param {Object} arg The callback argument object passed to the load function.
9345          */
9346         load : true,
9347         /**
9348          * @event loadexception
9349          * Fires if an Exception occurs during data retrieval.
9350          * @param {Object} This DataProxy object.
9351          * @param {Object} o The data object.
9352          * @param {Object} arg The callback argument object passed to the load function.
9353          * @param {Object} e The Exception.
9354          */
9355         loadexception : true
9356     });
9357     Roo.data.DataProxy.superclass.constructor.call(this);
9358 };
9359
9360 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
9361
9362     /**
9363      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
9364      */
9365 /*
9366  * Based on:
9367  * Ext JS Library 1.1.1
9368  * Copyright(c) 2006-2007, Ext JS, LLC.
9369  *
9370  * Originally Released Under LGPL - original licence link has changed is not relivant.
9371  *
9372  * Fork - LGPL
9373  * <script type="text/javascript">
9374  */
9375 /**
9376  * @class Roo.data.MemoryProxy
9377  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
9378  * to the Reader when its load method is called.
9379  * @constructor
9380  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
9381  */
9382 Roo.data.MemoryProxy = function(data){
9383     if (data.data) {
9384         data = data.data;
9385     }
9386     Roo.data.MemoryProxy.superclass.constructor.call(this);
9387     this.data = data;
9388 };
9389
9390 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
9391     /**
9392      * Load data from the requested source (in this case an in-memory
9393      * data object passed to the constructor), read the data object into
9394      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9395      * process that block using the passed callback.
9396      * @param {Object} params This parameter is not used by the MemoryProxy class.
9397      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9398      * object into a block of Roo.data.Records.
9399      * @param {Function} callback The function into which to pass the block of Roo.data.records.
9400      * The function must be passed <ul>
9401      * <li>The Record block object</li>
9402      * <li>The "arg" argument from the load function</li>
9403      * <li>A boolean success indicator</li>
9404      * </ul>
9405      * @param {Object} scope The scope in which to call the callback
9406      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9407      */
9408     load : function(params, reader, callback, scope, arg){
9409         params = params || {};
9410         var result;
9411         try {
9412             result = reader.readRecords(this.data);
9413         }catch(e){
9414             this.fireEvent("loadexception", this, arg, null, e);
9415             callback.call(scope, null, arg, false);
9416             return;
9417         }
9418         callback.call(scope, result, arg, true);
9419     },
9420     
9421     // private
9422     update : function(params, records){
9423         
9424     }
9425 });/*
9426  * Based on:
9427  * Ext JS Library 1.1.1
9428  * Copyright(c) 2006-2007, Ext JS, LLC.
9429  *
9430  * Originally Released Under LGPL - original licence link has changed is not relivant.
9431  *
9432  * Fork - LGPL
9433  * <script type="text/javascript">
9434  */
9435 /**
9436  * @class Roo.data.HttpProxy
9437  * @extends Roo.data.DataProxy
9438  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
9439  * configured to reference a certain URL.<br><br>
9440  * <p>
9441  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
9442  * from which the running page was served.<br><br>
9443  * <p>
9444  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
9445  * <p>
9446  * Be aware that to enable the browser to parse an XML document, the server must set
9447  * the Content-Type header in the HTTP response to "text/xml".
9448  * @constructor
9449  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
9450  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
9451  * will be used to make the request.
9452  */
9453 Roo.data.HttpProxy = function(conn){
9454     Roo.data.HttpProxy.superclass.constructor.call(this);
9455     // is conn a conn config or a real conn?
9456     this.conn = conn;
9457     this.useAjax = !conn || !conn.events;
9458   
9459 };
9460
9461 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
9462     // thse are take from connection...
9463     
9464     /**
9465      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
9466      */
9467     /**
9468      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
9469      * extra parameters to each request made by this object. (defaults to undefined)
9470      */
9471     /**
9472      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
9473      *  to each request made by this object. (defaults to undefined)
9474      */
9475     /**
9476      * @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)
9477      */
9478     /**
9479      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
9480      */
9481      /**
9482      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
9483      * @type Boolean
9484      */
9485   
9486
9487     /**
9488      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
9489      * @type Boolean
9490      */
9491     /**
9492      * Return the {@link Roo.data.Connection} object being used by this Proxy.
9493      * @return {Connection} The Connection object. This object may be used to subscribe to events on
9494      * a finer-grained basis than the DataProxy events.
9495      */
9496     getConnection : function(){
9497         return this.useAjax ? Roo.Ajax : this.conn;
9498     },
9499
9500     /**
9501      * Load data from the configured {@link Roo.data.Connection}, read the data object into
9502      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
9503      * process that block using the passed callback.
9504      * @param {Object} params An object containing properties which are to be used as HTTP parameters
9505      * for the request to the remote server.
9506      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9507      * object into a block of Roo.data.Records.
9508      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9509      * The function must be passed <ul>
9510      * <li>The Record block object</li>
9511      * <li>The "arg" argument from the load function</li>
9512      * <li>A boolean success indicator</li>
9513      * </ul>
9514      * @param {Object} scope The scope in which to call the callback
9515      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9516      */
9517     load : function(params, reader, callback, scope, arg){
9518         if(this.fireEvent("beforeload", this, params) !== false){
9519             var  o = {
9520                 params : params || {},
9521                 request: {
9522                     callback : callback,
9523                     scope : scope,
9524                     arg : arg
9525                 },
9526                 reader: reader,
9527                 callback : this.loadResponse,
9528                 scope: this
9529             };
9530             if(this.useAjax){
9531                 Roo.applyIf(o, this.conn);
9532                 if(this.activeRequest){
9533                     Roo.Ajax.abort(this.activeRequest);
9534                 }
9535                 this.activeRequest = Roo.Ajax.request(o);
9536             }else{
9537                 this.conn.request(o);
9538             }
9539         }else{
9540             callback.call(scope||this, null, arg, false);
9541         }
9542     },
9543
9544     // private
9545     loadResponse : function(o, success, response){
9546         delete this.activeRequest;
9547         if(!success){
9548             this.fireEvent("loadexception", this, o, response);
9549             o.request.callback.call(o.request.scope, null, o.request.arg, false);
9550             return;
9551         }
9552         var result;
9553         try {
9554             result = o.reader.read(response);
9555         }catch(e){
9556             this.fireEvent("loadexception", this, o, response, e);
9557             o.request.callback.call(o.request.scope, null, o.request.arg, false);
9558             return;
9559         }
9560         
9561         this.fireEvent("load", this, o, o.request.arg);
9562         o.request.callback.call(o.request.scope, result, o.request.arg, true);
9563     },
9564
9565     // private
9566     update : function(dataSet){
9567
9568     },
9569
9570     // private
9571     updateResponse : function(dataSet){
9572
9573     }
9574 });/*
9575  * Based on:
9576  * Ext JS Library 1.1.1
9577  * Copyright(c) 2006-2007, Ext JS, LLC.
9578  *
9579  * Originally Released Under LGPL - original licence link has changed is not relivant.
9580  *
9581  * Fork - LGPL
9582  * <script type="text/javascript">
9583  */
9584
9585 /**
9586  * @class Roo.data.ScriptTagProxy
9587  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
9588  * other than the originating domain of the running page.<br><br>
9589  * <p>
9590  * <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
9591  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
9592  * <p>
9593  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
9594  * source code that is used as the source inside a &lt;script> tag.<br><br>
9595  * <p>
9596  * In order for the browser to process the returned data, the server must wrap the data object
9597  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
9598  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
9599  * depending on whether the callback name was passed:
9600  * <p>
9601  * <pre><code>
9602 boolean scriptTag = false;
9603 String cb = request.getParameter("callback");
9604 if (cb != null) {
9605     scriptTag = true;
9606     response.setContentType("text/javascript");
9607 } else {
9608     response.setContentType("application/x-json");
9609 }
9610 Writer out = response.getWriter();
9611 if (scriptTag) {
9612     out.write(cb + "(");
9613 }
9614 out.print(dataBlock.toJsonString());
9615 if (scriptTag) {
9616     out.write(");");
9617 }
9618 </pre></code>
9619  *
9620  * @constructor
9621  * @param {Object} config A configuration object.
9622  */
9623 Roo.data.ScriptTagProxy = function(config){
9624     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
9625     Roo.apply(this, config);
9626     this.head = document.getElementsByTagName("head")[0];
9627 };
9628
9629 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
9630
9631 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
9632     /**
9633      * @cfg {String} url The URL from which to request the data object.
9634      */
9635     /**
9636      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
9637      */
9638     timeout : 30000,
9639     /**
9640      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
9641      * the server the name of the callback function set up by the load call to process the returned data object.
9642      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
9643      * javascript output which calls this named function passing the data object as its only parameter.
9644      */
9645     callbackParam : "callback",
9646     /**
9647      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
9648      * name to the request.
9649      */
9650     nocache : true,
9651
9652     /**
9653      * Load data from the configured URL, read the data object into
9654      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9655      * process that block using the passed callback.
9656      * @param {Object} params An object containing properties which are to be used as HTTP parameters
9657      * for the request to the remote server.
9658      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9659      * object into a block of Roo.data.Records.
9660      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9661      * The function must be passed <ul>
9662      * <li>The Record block object</li>
9663      * <li>The "arg" argument from the load function</li>
9664      * <li>A boolean success indicator</li>
9665      * </ul>
9666      * @param {Object} scope The scope in which to call the callback
9667      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9668      */
9669     load : function(params, reader, callback, scope, arg){
9670         if(this.fireEvent("beforeload", this, params) !== false){
9671
9672             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
9673
9674             var url = this.url;
9675             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
9676             if(this.nocache){
9677                 url += "&_dc=" + (new Date().getTime());
9678             }
9679             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
9680             var trans = {
9681                 id : transId,
9682                 cb : "stcCallback"+transId,
9683                 scriptId : "stcScript"+transId,
9684                 params : params,
9685                 arg : arg,
9686                 url : url,
9687                 callback : callback,
9688                 scope : scope,
9689                 reader : reader
9690             };
9691             var conn = this;
9692
9693             window[trans.cb] = function(o){
9694                 conn.handleResponse(o, trans);
9695             };
9696
9697             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
9698
9699             if(this.autoAbort !== false){
9700                 this.abort();
9701             }
9702
9703             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
9704
9705             var script = document.createElement("script");
9706             script.setAttribute("src", url);
9707             script.setAttribute("type", "text/javascript");
9708             script.setAttribute("id", trans.scriptId);
9709             this.head.appendChild(script);
9710
9711             this.trans = trans;
9712         }else{
9713             callback.call(scope||this, null, arg, false);
9714         }
9715     },
9716
9717     // private
9718     isLoading : function(){
9719         return this.trans ? true : false;
9720     },
9721
9722     /**
9723      * Abort the current server request.
9724      */
9725     abort : function(){
9726         if(this.isLoading()){
9727             this.destroyTrans(this.trans);
9728         }
9729     },
9730
9731     // private
9732     destroyTrans : function(trans, isLoaded){
9733         this.head.removeChild(document.getElementById(trans.scriptId));
9734         clearTimeout(trans.timeoutId);
9735         if(isLoaded){
9736             window[trans.cb] = undefined;
9737             try{
9738                 delete window[trans.cb];
9739             }catch(e){}
9740         }else{
9741             // if hasn't been loaded, wait for load to remove it to prevent script error
9742             window[trans.cb] = function(){
9743                 window[trans.cb] = undefined;
9744                 try{
9745                     delete window[trans.cb];
9746                 }catch(e){}
9747             };
9748         }
9749     },
9750
9751     // private
9752     handleResponse : function(o, trans){
9753         this.trans = false;
9754         this.destroyTrans(trans, true);
9755         var result;
9756         try {
9757             result = trans.reader.readRecords(o);
9758         }catch(e){
9759             this.fireEvent("loadexception", this, o, trans.arg, e);
9760             trans.callback.call(trans.scope||window, null, trans.arg, false);
9761             return;
9762         }
9763         this.fireEvent("load", this, o, trans.arg);
9764         trans.callback.call(trans.scope||window, result, trans.arg, true);
9765     },
9766
9767     // private
9768     handleFailure : function(trans){
9769         this.trans = false;
9770         this.destroyTrans(trans, false);
9771         this.fireEvent("loadexception", this, null, trans.arg);
9772         trans.callback.call(trans.scope||window, null, trans.arg, false);
9773     }
9774 });/*
9775  * Based on:
9776  * Ext JS Library 1.1.1
9777  * Copyright(c) 2006-2007, Ext JS, LLC.
9778  *
9779  * Originally Released Under LGPL - original licence link has changed is not relivant.
9780  *
9781  * Fork - LGPL
9782  * <script type="text/javascript">
9783  */
9784
9785 /**
9786  * @class Roo.data.JsonReader
9787  * @extends Roo.data.DataReader
9788  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
9789  * based on mappings in a provided Roo.data.Record constructor.
9790  * 
9791  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
9792  * in the reply previously. 
9793  * 
9794  * <p>
9795  * Example code:
9796  * <pre><code>
9797 var RecordDef = Roo.data.Record.create([
9798     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
9799     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
9800 ]);
9801 var myReader = new Roo.data.JsonReader({
9802     totalProperty: "results",    // The property which contains the total dataset size (optional)
9803     root: "rows",                // The property which contains an Array of row objects
9804     id: "id"                     // The property within each row object that provides an ID for the record (optional)
9805 }, RecordDef);
9806 </code></pre>
9807  * <p>
9808  * This would consume a JSON file like this:
9809  * <pre><code>
9810 { 'results': 2, 'rows': [
9811     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
9812     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
9813 }
9814 </code></pre>
9815  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
9816  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
9817  * paged from the remote server.
9818  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
9819  * @cfg {String} root name of the property which contains the Array of row objects.
9820  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
9821  * @constructor
9822  * Create a new JsonReader
9823  * @param {Object} meta Metadata configuration options
9824  * @param {Object} recordType Either an Array of field definition objects,
9825  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
9826  */
9827 Roo.data.JsonReader = function(meta, recordType){
9828     
9829     meta = meta || {};
9830     // set some defaults:
9831     Roo.applyIf(meta, {
9832         totalProperty: 'total',
9833         successProperty : 'success',
9834         root : 'data',
9835         id : 'id'
9836     });
9837     
9838     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
9839 };
9840 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
9841     
9842     /**
9843      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
9844      * Used by Store query builder to append _requestMeta to params.
9845      * 
9846      */
9847     metaFromRemote : false,
9848     /**
9849      * This method is only used by a DataProxy which has retrieved data from a remote server.
9850      * @param {Object} response The XHR object which contains the JSON data in its responseText.
9851      * @return {Object} data A data block which is used by an Roo.data.Store object as
9852      * a cache of Roo.data.Records.
9853      */
9854     read : function(response){
9855         var json = response.responseText;
9856        
9857         var o = /* eval:var:o */ eval("("+json+")");
9858         if(!o) {
9859             throw {message: "JsonReader.read: Json object not found"};
9860         }
9861         
9862         if(o.metaData){
9863             
9864             delete this.ef;
9865             this.metaFromRemote = true;
9866             this.meta = o.metaData;
9867             this.recordType = Roo.data.Record.create(o.metaData.fields);
9868             this.onMetaChange(this.meta, this.recordType, o);
9869         }
9870         return this.readRecords(o);
9871     },
9872
9873     // private function a store will implement
9874     onMetaChange : function(meta, recordType, o){
9875
9876     },
9877
9878     /**
9879          * @ignore
9880          */
9881     simpleAccess: function(obj, subsc) {
9882         return obj[subsc];
9883     },
9884
9885         /**
9886          * @ignore
9887          */
9888     getJsonAccessor: function(){
9889         var re = /[\[\.]/;
9890         return function(expr) {
9891             try {
9892                 return(re.test(expr))
9893                     ? new Function("obj", "return obj." + expr)
9894                     : function(obj){
9895                         return obj[expr];
9896                     };
9897             } catch(e){}
9898             return Roo.emptyFn;
9899         };
9900     }(),
9901
9902     /**
9903      * Create a data block containing Roo.data.Records from an XML document.
9904      * @param {Object} o An object which contains an Array of row objects in the property specified
9905      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
9906      * which contains the total size of the dataset.
9907      * @return {Object} data A data block which is used by an Roo.data.Store object as
9908      * a cache of Roo.data.Records.
9909      */
9910     readRecords : function(o){
9911         /**
9912          * After any data loads, the raw JSON data is available for further custom processing.
9913          * @type Object
9914          */
9915         this.o = o;
9916         var s = this.meta, Record = this.recordType,
9917             f = Record.prototype.fields, fi = f.items, fl = f.length;
9918
9919 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
9920         if (!this.ef) {
9921             if(s.totalProperty) {
9922                     this.getTotal = this.getJsonAccessor(s.totalProperty);
9923                 }
9924                 if(s.successProperty) {
9925                     this.getSuccess = this.getJsonAccessor(s.successProperty);
9926                 }
9927                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
9928                 if (s.id) {
9929                         var g = this.getJsonAccessor(s.id);
9930                         this.getId = function(rec) {
9931                                 var r = g(rec);
9932                                 return (r === undefined || r === "") ? null : r;
9933                         };
9934                 } else {
9935                         this.getId = function(){return null;};
9936                 }
9937             this.ef = [];
9938             for(var jj = 0; jj < fl; jj++){
9939                 f = fi[jj];
9940                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
9941                 this.ef[jj] = this.getJsonAccessor(map);
9942             }
9943         }
9944
9945         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
9946         if(s.totalProperty){
9947             var vt = parseInt(this.getTotal(o), 10);
9948             if(!isNaN(vt)){
9949                 totalRecords = vt;
9950             }
9951         }
9952         if(s.successProperty){
9953             var vs = this.getSuccess(o);
9954             if(vs === false || vs === 'false'){
9955                 success = false;
9956             }
9957         }
9958         var records = [];
9959             for(var i = 0; i < c; i++){
9960                     var n = root[i];
9961                 var values = {};
9962                 var id = this.getId(n);
9963                 for(var j = 0; j < fl; j++){
9964                     f = fi[j];
9965                 var v = this.ef[j](n);
9966                 if (!f.convert) {
9967                     Roo.log('missing convert for ' + f.name);
9968                     Roo.log(f);
9969                     continue;
9970                 }
9971                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
9972                 }
9973                 var record = new Record(values, id);
9974                 record.json = n;
9975                 records[i] = record;
9976             }
9977             return {
9978             raw : o,
9979                 success : success,
9980                 records : records,
9981                 totalRecords : totalRecords
9982             };
9983     }
9984 });/*
9985  * Based on:
9986  * Ext JS Library 1.1.1
9987  * Copyright(c) 2006-2007, Ext JS, LLC.
9988  *
9989  * Originally Released Under LGPL - original licence link has changed is not relivant.
9990  *
9991  * Fork - LGPL
9992  * <script type="text/javascript">
9993  */
9994
9995 /**
9996  * @class Roo.data.ArrayReader
9997  * @extends Roo.data.DataReader
9998  * Data reader class to create an Array of Roo.data.Record objects from an Array.
9999  * Each element of that Array represents a row of data fields. The
10000  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
10001  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
10002  * <p>
10003  * Example code:.
10004  * <pre><code>
10005 var RecordDef = Roo.data.Record.create([
10006     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
10007     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
10008 ]);
10009 var myReader = new Roo.data.ArrayReader({
10010     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
10011 }, RecordDef);
10012 </code></pre>
10013  * <p>
10014  * This would consume an Array like this:
10015  * <pre><code>
10016 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
10017   </code></pre>
10018  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
10019  * @constructor
10020  * Create a new JsonReader
10021  * @param {Object} meta Metadata configuration options.
10022  * @param {Object} recordType Either an Array of field definition objects
10023  * as specified to {@link Roo.data.Record#create},
10024  * or an {@link Roo.data.Record} object
10025  * created using {@link Roo.data.Record#create}.
10026  */
10027 Roo.data.ArrayReader = function(meta, recordType){
10028     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
10029 };
10030
10031 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
10032     /**
10033      * Create a data block containing Roo.data.Records from an XML document.
10034      * @param {Object} o An Array of row objects which represents the dataset.
10035      * @return {Object} data A data block which is used by an Roo.data.Store object as
10036      * a cache of Roo.data.Records.
10037      */
10038     readRecords : function(o){
10039         var sid = this.meta ? this.meta.id : null;
10040         var recordType = this.recordType, fields = recordType.prototype.fields;
10041         var records = [];
10042         var root = o;
10043             for(var i = 0; i < root.length; i++){
10044                     var n = root[i];
10045                 var values = {};
10046                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
10047                 for(var j = 0, jlen = fields.length; j < jlen; j++){
10048                 var f = fields.items[j];
10049                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
10050                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
10051                 v = f.convert(v);
10052                 values[f.name] = v;
10053             }
10054                 var record = new recordType(values, id);
10055                 record.json = n;
10056                 records[records.length] = record;
10057             }
10058             return {
10059                 records : records,
10060                 totalRecords : records.length
10061             };
10062     }
10063 });/*
10064  * - LGPL
10065  * * 
10066  */
10067
10068 /**
10069  * @class Roo.bootstrap.ComboBox
10070  * @extends Roo.bootstrap.TriggerField
10071  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
10072  * @cfg {Boolean} append (true|false) default false
10073  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
10074  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
10075  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
10076  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
10077  * @constructor
10078  * Create a new ComboBox.
10079  * @param {Object} config Configuration options
10080  */
10081 Roo.bootstrap.ComboBox = function(config){
10082     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
10083     this.addEvents({
10084         /**
10085          * @event expand
10086          * Fires when the dropdown list is expanded
10087              * @param {Roo.bootstrap.ComboBox} combo This combo box
10088              */
10089         'expand' : true,
10090         /**
10091          * @event collapse
10092          * Fires when the dropdown list is collapsed
10093              * @param {Roo.bootstrap.ComboBox} combo This combo box
10094              */
10095         'collapse' : true,
10096         /**
10097          * @event beforeselect
10098          * Fires before a list item is selected. Return false to cancel the selection.
10099              * @param {Roo.bootstrap.ComboBox} combo This combo box
10100              * @param {Roo.data.Record} record The data record returned from the underlying store
10101              * @param {Number} index The index of the selected item in the dropdown list
10102              */
10103         'beforeselect' : true,
10104         /**
10105          * @event select
10106          * Fires when a list item is selected
10107              * @param {Roo.bootstrap.ComboBox} combo This combo box
10108              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
10109              * @param {Number} index The index of the selected item in the dropdown list
10110              */
10111         'select' : true,
10112         /**
10113          * @event beforequery
10114          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
10115          * The event object passed has these properties:
10116              * @param {Roo.bootstrap.ComboBox} combo This combo box
10117              * @param {String} query The query
10118              * @param {Boolean} forceAll true to force "all" query
10119              * @param {Boolean} cancel true to cancel the query
10120              * @param {Object} e The query event object
10121              */
10122         'beforequery': true,
10123          /**
10124          * @event add
10125          * Fires when the 'add' icon is pressed (add a listener to enable add button)
10126              * @param {Roo.bootstrap.ComboBox} combo This combo box
10127              */
10128         'add' : true,
10129         /**
10130          * @event edit
10131          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
10132              * @param {Roo.bootstrap.ComboBox} combo This combo box
10133              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
10134              */
10135         'edit' : true,
10136         /**
10137          * @event remove
10138          * Fires when the remove value from the combobox array
10139              * @param {Roo.bootstrap.ComboBox} combo This combo box
10140              */
10141         'remove' : true
10142         
10143     });
10144     
10145     this.item = [];
10146     this.tickItems = [];
10147     
10148     this.selectedIndex = -1;
10149     if(this.mode == 'local'){
10150         if(config.queryDelay === undefined){
10151             this.queryDelay = 10;
10152         }
10153         if(config.minChars === undefined){
10154             this.minChars = 0;
10155         }
10156     }
10157 };
10158
10159 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
10160      
10161     /**
10162      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
10163      * rendering into an Roo.Editor, defaults to false)
10164      */
10165     /**
10166      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
10167      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
10168      */
10169     /**
10170      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
10171      */
10172     /**
10173      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
10174      * the dropdown list (defaults to undefined, with no header element)
10175      */
10176
10177      /**
10178      * @cfg {String/Roo.Template} tpl The template to use to render the output
10179      */
10180      
10181      /**
10182      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
10183      */
10184     listWidth: undefined,
10185     /**
10186      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
10187      * mode = 'remote' or 'text' if mode = 'local')
10188      */
10189     displayField: undefined,
10190     /**
10191      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
10192      * mode = 'remote' or 'value' if mode = 'local'). 
10193      * Note: use of a valueField requires the user make a selection
10194      * in order for a value to be mapped.
10195      */
10196     valueField: undefined,
10197     
10198     
10199     /**
10200      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
10201      * field's data value (defaults to the underlying DOM element's name)
10202      */
10203     hiddenName: undefined,
10204     /**
10205      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
10206      */
10207     listClass: '',
10208     /**
10209      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
10210      */
10211     selectedClass: 'active',
10212     
10213     /**
10214      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
10215      */
10216     shadow:'sides',
10217     /**
10218      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
10219      * anchor positions (defaults to 'tl-bl')
10220      */
10221     listAlign: 'tl-bl?',
10222     /**
10223      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
10224      */
10225     maxHeight: 300,
10226     /**
10227      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
10228      * query specified by the allQuery config option (defaults to 'query')
10229      */
10230     triggerAction: 'query',
10231     /**
10232      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
10233      * (defaults to 4, does not apply if editable = false)
10234      */
10235     minChars : 4,
10236     /**
10237      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
10238      * delay (typeAheadDelay) if it matches a known value (defaults to false)
10239      */
10240     typeAhead: false,
10241     /**
10242      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
10243      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
10244      */
10245     queryDelay: 500,
10246     /**
10247      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
10248      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
10249      */
10250     pageSize: 0,
10251     /**
10252      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
10253      * when editable = true (defaults to false)
10254      */
10255     selectOnFocus:false,
10256     /**
10257      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
10258      */
10259     queryParam: 'query',
10260     /**
10261      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
10262      * when mode = 'remote' (defaults to 'Loading...')
10263      */
10264     loadingText: 'Loading...',
10265     /**
10266      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
10267      */
10268     resizable: false,
10269     /**
10270      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
10271      */
10272     handleHeight : 8,
10273     /**
10274      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
10275      * traditional select (defaults to true)
10276      */
10277     editable: true,
10278     /**
10279      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
10280      */
10281     allQuery: '',
10282     /**
10283      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
10284      */
10285     mode: 'remote',
10286     /**
10287      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
10288      * listWidth has a higher value)
10289      */
10290     minListWidth : 70,
10291     /**
10292      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
10293      * allow the user to set arbitrary text into the field (defaults to false)
10294      */
10295     forceSelection:false,
10296     /**
10297      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
10298      * if typeAhead = true (defaults to 250)
10299      */
10300     typeAheadDelay : 250,
10301     /**
10302      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
10303      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
10304      */
10305     valueNotFoundText : undefined,
10306     /**
10307      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
10308      */
10309     blockFocus : false,
10310     
10311     /**
10312      * @cfg {Boolean} disableClear Disable showing of clear button.
10313      */
10314     disableClear : false,
10315     /**
10316      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
10317      */
10318     alwaysQuery : false,
10319     
10320     /**
10321      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
10322      */
10323     multiple : false,
10324     
10325     //private
10326     addicon : false,
10327     editicon: false,
10328     
10329     page: 0,
10330     hasQuery: false,
10331     append: false,
10332     loadNext: false,
10333     autoFocus : true,
10334     tickable : false,
10335     btnPosition : 'right',
10336     triggerList : true,
10337     // element that contains real text value.. (when hidden is used..)
10338     
10339     getAutoCreate : function()
10340     {
10341         var cfg = false;
10342         
10343         /*
10344          *  Normal ComboBox
10345          */
10346         if(!this.tickable){
10347             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
10348             return cfg;
10349         }
10350         
10351         /*
10352          *  ComboBox with tickable selections
10353          */
10354              
10355         var align = this.labelAlign || this.parentLabelAlign();
10356         
10357         cfg = {
10358             cls : 'form-group roo-combobox-tickable' //input-group
10359         };
10360         
10361         
10362         var buttons = {
10363             tag : 'div',
10364             cls : 'tickable-buttons',
10365             cn : [
10366                 {
10367                     tag : 'button',
10368                     type : 'button',
10369                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
10370                     html : 'Edit'
10371                 },
10372                 {
10373                     tag : 'button',
10374                     type : 'button',
10375                     name : 'ok',
10376                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
10377                     html : 'Done'
10378                 },
10379                 {
10380                     tag : 'button',
10381                     type : 'button',
10382                     name : 'cancel',
10383                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
10384                     html : 'Cancel'
10385                 }
10386             ]
10387         };
10388         
10389         var _this = this;
10390         Roo.each(buttons.cn, function(c){
10391             if (_this.size) {
10392                 c.cls += ' btn-' + _this.size;
10393             }
10394
10395             if (_this.disabled) {
10396                 c.disabled = true;
10397             }
10398         });
10399         
10400         var box = {
10401             tag: 'div',
10402             cn: [
10403                 {
10404                     tag: 'input',
10405                     type : 'hidden',
10406                     cls: 'form-hidden-field'
10407                 },
10408                 {
10409                     tag: 'ul',
10410                     cls: 'select2-choices',
10411                     cn:[
10412                         {
10413                             tag: 'li',
10414                             cls: 'select2-search-field',
10415                             cn: [
10416
10417                                 buttons
10418                             ]
10419                         }
10420                     ]
10421                 }
10422             ]
10423         }
10424         
10425         var combobox = {
10426             cls: 'select2-container input-group select2-container-multi',
10427             cn: [
10428                 box
10429 //                {
10430 //                    tag: 'ul',
10431 //                    cls: 'typeahead typeahead-long dropdown-menu',
10432 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
10433 //                }
10434             ]
10435         };
10436         
10437         if (align ==='left' && this.fieldLabel.length) {
10438             
10439                 Roo.log("left and has label");
10440                 cfg.cn = [
10441                     
10442                     {
10443                         tag: 'label',
10444                         'for' :  id,
10445                         cls : 'control-label col-sm-' + this.labelWidth,
10446                         html : this.fieldLabel
10447                         
10448                     },
10449                     {
10450                         cls : "col-sm-" + (12 - this.labelWidth), 
10451                         cn: [
10452                             combobox
10453                         ]
10454                     }
10455                     
10456                 ];
10457         } else if ( this.fieldLabel.length) {
10458                 Roo.log(" label");
10459                  cfg.cn = [
10460                    
10461                     {
10462                         tag: 'label',
10463                         //cls : 'input-group-addon',
10464                         html : this.fieldLabel
10465                         
10466                     },
10467                     
10468                     combobox
10469                     
10470                 ];
10471
10472         } else {
10473             
10474                 Roo.log(" no label && no align");
10475                 cfg = combobox
10476                      
10477                 
10478         }
10479          
10480         var settings=this;
10481         ['xs','sm','md','lg'].map(function(size){
10482             if (settings[size]) {
10483                 cfg.cls += ' col-' + size + '-' + settings[size];
10484             }
10485         });
10486         
10487         return cfg;
10488         
10489     },
10490     
10491     // private
10492     initEvents: function()
10493     {
10494         
10495         if (!this.store) {
10496             throw "can not find store for combo";
10497         }
10498         this.store = Roo.factory(this.store, Roo.data);
10499         
10500         if(this.tickable){
10501             this.initTickableEvnets();
10502             return;
10503         }
10504         
10505         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
10506         
10507         if(this.hiddenName){
10508             
10509             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10510             
10511             this.hiddenField.dom.value =
10512                 this.hiddenValue !== undefined ? this.hiddenValue :
10513                 this.value !== undefined ? this.value : '';
10514
10515             // prevent input submission
10516             this.el.dom.removeAttribute('name');
10517             this.hiddenField.dom.setAttribute('name', this.hiddenName);
10518              
10519              
10520         }
10521         //if(Roo.isGecko){
10522         //    this.el.dom.setAttribute('autocomplete', 'off');
10523         //}
10524         
10525         var cls = 'x-combo-list';
10526         
10527         //this.list = new Roo.Layer({
10528         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
10529         //});
10530         
10531         var _this = this;
10532         
10533         (function(){
10534             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
10535             _this.list.setWidth(lw);
10536         }).defer(100);
10537         
10538         this.list.on('mouseover', this.onViewOver, this);
10539         this.list.on('mousemove', this.onViewMove, this);
10540         
10541         this.list.on('scroll', this.onViewScroll, this);
10542         
10543         /*
10544         this.list.swallowEvent('mousewheel');
10545         this.assetHeight = 0;
10546
10547         if(this.title){
10548             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
10549             this.assetHeight += this.header.getHeight();
10550         }
10551
10552         this.innerList = this.list.createChild({cls:cls+'-inner'});
10553         this.innerList.on('mouseover', this.onViewOver, this);
10554         this.innerList.on('mousemove', this.onViewMove, this);
10555         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
10556         
10557         if(this.allowBlank && !this.pageSize && !this.disableClear){
10558             this.footer = this.list.createChild({cls:cls+'-ft'});
10559             this.pageTb = new Roo.Toolbar(this.footer);
10560            
10561         }
10562         if(this.pageSize){
10563             this.footer = this.list.createChild({cls:cls+'-ft'});
10564             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
10565                     {pageSize: this.pageSize});
10566             
10567         }
10568         
10569         if (this.pageTb && this.allowBlank && !this.disableClear) {
10570             var _this = this;
10571             this.pageTb.add(new Roo.Toolbar.Fill(), {
10572                 cls: 'x-btn-icon x-btn-clear',
10573                 text: '&#160;',
10574                 handler: function()
10575                 {
10576                     _this.collapse();
10577                     _this.clearValue();
10578                     _this.onSelect(false, -1);
10579                 }
10580             });
10581         }
10582         if (this.footer) {
10583             this.assetHeight += this.footer.getHeight();
10584         }
10585         */
10586             
10587         if(!this.tpl){
10588             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
10589         }
10590
10591         this.view = new Roo.View(this.list, this.tpl, {
10592             singleSelect:true, store: this.store, selectedClass: this.selectedClass
10593         });
10594         //this.view.wrapEl.setDisplayed(false);
10595         this.view.on('click', this.onViewClick, this);
10596         
10597         
10598         
10599         this.store.on('beforeload', this.onBeforeLoad, this);
10600         this.store.on('load', this.onLoad, this);
10601         this.store.on('loadexception', this.onLoadException, this);
10602         /*
10603         if(this.resizable){
10604             this.resizer = new Roo.Resizable(this.list,  {
10605                pinned:true, handles:'se'
10606             });
10607             this.resizer.on('resize', function(r, w, h){
10608                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
10609                 this.listWidth = w;
10610                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
10611                 this.restrictHeight();
10612             }, this);
10613             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
10614         }
10615         */
10616         if(!this.editable){
10617             this.editable = true;
10618             this.setEditable(false);
10619         }
10620         
10621         /*
10622         
10623         if (typeof(this.events.add.listeners) != 'undefined') {
10624             
10625             this.addicon = this.wrap.createChild(
10626                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
10627        
10628             this.addicon.on('click', function(e) {
10629                 this.fireEvent('add', this);
10630             }, this);
10631         }
10632         if (typeof(this.events.edit.listeners) != 'undefined') {
10633             
10634             this.editicon = this.wrap.createChild(
10635                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
10636             if (this.addicon) {
10637                 this.editicon.setStyle('margin-left', '40px');
10638             }
10639             this.editicon.on('click', function(e) {
10640                 
10641                 // we fire even  if inothing is selected..
10642                 this.fireEvent('edit', this, this.lastData );
10643                 
10644             }, this);
10645         }
10646         */
10647         
10648         this.keyNav = new Roo.KeyNav(this.inputEl(), {
10649             "up" : function(e){
10650                 this.inKeyMode = true;
10651                 this.selectPrev();
10652             },
10653
10654             "down" : function(e){
10655                 if(!this.isExpanded()){
10656                     this.onTriggerClick();
10657                 }else{
10658                     this.inKeyMode = true;
10659                     this.selectNext();
10660                 }
10661             },
10662
10663             "enter" : function(e){
10664 //                this.onViewClick();
10665                 //return true;
10666                 this.collapse();
10667                 
10668                 if(this.fireEvent("specialkey", this, e)){
10669                     this.onViewClick(false);
10670                 }
10671                 
10672                 return true;
10673             },
10674
10675             "esc" : function(e){
10676                 this.collapse();
10677             },
10678
10679             "tab" : function(e){
10680                 this.collapse();
10681                 
10682                 if(this.fireEvent("specialkey", this, e)){
10683                     this.onViewClick(false);
10684                 }
10685                 
10686                 return true;
10687             },
10688
10689             scope : this,
10690
10691             doRelay : function(foo, bar, hname){
10692                 if(hname == 'down' || this.scope.isExpanded()){
10693                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
10694                 }
10695                 return true;
10696             },
10697
10698             forceKeyDown: true
10699         });
10700         
10701         
10702         this.queryDelay = Math.max(this.queryDelay || 10,
10703                 this.mode == 'local' ? 10 : 250);
10704         
10705         
10706         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
10707         
10708         if(this.typeAhead){
10709             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
10710         }
10711         if(this.editable !== false){
10712             this.inputEl().on("keyup", this.onKeyUp, this);
10713         }
10714         if(this.forceSelection){
10715             this.inputEl().on('blur', this.doForce, this);
10716         }
10717         
10718         if(this.multiple){
10719             this.choices = this.el.select('ul.select2-choices', true).first();
10720             this.searchField = this.el.select('ul li.select2-search-field', true).first();
10721         }
10722     },
10723     
10724     initTickableEvnets: function()
10725     {   
10726         this.createList();
10727         
10728         if(this.hiddenName){
10729             
10730             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10731             
10732             this.hiddenField.dom.value =
10733                 this.hiddenValue !== undefined ? this.hiddenValue :
10734                 this.value !== undefined ? this.value : '';
10735
10736             // prevent input submission
10737             this.el.dom.removeAttribute('name');
10738             this.hiddenField.dom.setAttribute('name', this.hiddenName);
10739              
10740              
10741         }
10742         
10743 //        this.list = this.el.select('ul.dropdown-menu',true).first();
10744         
10745         this.choices = this.el.select('ul.select2-choices', true).first();
10746         this.searchField = this.el.select('ul li.select2-search-field', true).first();
10747         if(this.triggerList){
10748             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
10749         }
10750          
10751         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
10752         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
10753         
10754         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
10755         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
10756         
10757         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
10758         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
10759         
10760         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
10761         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
10762         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
10763         
10764         this.okBtn.hide();
10765         this.cancelBtn.hide();
10766         
10767         var _this = this;
10768         
10769         (function(){
10770             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
10771             _this.list.setWidth(lw);
10772         }).defer(100);
10773         
10774         this.list.on('mouseover', this.onViewOver, this);
10775         this.list.on('mousemove', this.onViewMove, this);
10776         
10777         this.list.on('scroll', this.onViewScroll, this);
10778         
10779         if(!this.tpl){
10780             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>';
10781         }
10782
10783         this.view = new Roo.View(this.list, this.tpl, {
10784             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
10785         });
10786         
10787         //this.view.wrapEl.setDisplayed(false);
10788         this.view.on('click', this.onViewClick, this);
10789         
10790         
10791         
10792         this.store.on('beforeload', this.onBeforeLoad, this);
10793         this.store.on('load', this.onLoad, this);
10794         this.store.on('loadexception', this.onLoadException, this);
10795         
10796 //        this.keyNav = new Roo.KeyNav(this.inputEl(), {
10797 //            "up" : function(e){
10798 //                this.inKeyMode = true;
10799 //                this.selectPrev();
10800 //            },
10801 //
10802 //            "down" : function(e){
10803 //                if(!this.isExpanded()){
10804 //                    this.onTriggerClick();
10805 //                }else{
10806 //                    this.inKeyMode = true;
10807 //                    this.selectNext();
10808 //                }
10809 //            },
10810 //
10811 //            "enter" : function(e){
10812 ////                this.onViewClick();
10813 //                //return true;
10814 //                this.collapse();
10815 //                
10816 //                if(this.fireEvent("specialkey", this, e)){
10817 //                    this.onViewClick(false);
10818 //                }
10819 //                
10820 //                return true;
10821 //            },
10822 //
10823 //            "esc" : function(e){
10824 //                this.collapse();
10825 //            },
10826 //
10827 //            "tab" : function(e){
10828 //                this.collapse();
10829 //                
10830 //                if(this.fireEvent("specialkey", this, e)){
10831 //                    this.onViewClick(false);
10832 //                }
10833 //                
10834 //                return true;
10835 //            },
10836 //
10837 //            scope : this,
10838 //
10839 //            doRelay : function(foo, bar, hname){
10840 //                if(hname == 'down' || this.scope.isExpanded()){
10841 //                   return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
10842 //                }
10843 //                return true;
10844 //            },
10845 //
10846 //            forceKeyDown: true
10847 //        });
10848         
10849         
10850         this.queryDelay = Math.max(this.queryDelay || 10,
10851                 this.mode == 'local' ? 10 : 250);
10852         
10853         
10854         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
10855         
10856         if(this.typeAhead){
10857             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
10858         }
10859     },
10860
10861     onDestroy : function(){
10862         if(this.view){
10863             this.view.setStore(null);
10864             this.view.el.removeAllListeners();
10865             this.view.el.remove();
10866             this.view.purgeListeners();
10867         }
10868         if(this.list){
10869             this.list.dom.innerHTML  = '';
10870         }
10871         
10872         if(this.store){
10873             this.store.un('beforeload', this.onBeforeLoad, this);
10874             this.store.un('load', this.onLoad, this);
10875             this.store.un('loadexception', this.onLoadException, this);
10876         }
10877         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
10878     },
10879
10880     // private
10881     fireKey : function(e){
10882         if(e.isNavKeyPress() && !this.list.isVisible()){
10883             this.fireEvent("specialkey", this, e);
10884         }
10885     },
10886
10887     // private
10888     onResize: function(w, h){
10889 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
10890 //        
10891 //        if(typeof w != 'number'){
10892 //            // we do not handle it!?!?
10893 //            return;
10894 //        }
10895 //        var tw = this.trigger.getWidth();
10896 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
10897 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
10898 //        var x = w - tw;
10899 //        this.inputEl().setWidth( this.adjustWidth('input', x));
10900 //            
10901 //        //this.trigger.setStyle('left', x+'px');
10902 //        
10903 //        if(this.list && this.listWidth === undefined){
10904 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
10905 //            this.list.setWidth(lw);
10906 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
10907 //        }
10908         
10909     
10910         
10911     },
10912
10913     /**
10914      * Allow or prevent the user from directly editing the field text.  If false is passed,
10915      * the user will only be able to select from the items defined in the dropdown list.  This method
10916      * is the runtime equivalent of setting the 'editable' config option at config time.
10917      * @param {Boolean} value True to allow the user to directly edit the field text
10918      */
10919     setEditable : function(value){
10920         if(value == this.editable){
10921             return;
10922         }
10923         this.editable = value;
10924         if(!value){
10925             this.inputEl().dom.setAttribute('readOnly', true);
10926             this.inputEl().on('mousedown', this.onTriggerClick,  this);
10927             this.inputEl().addClass('x-combo-noedit');
10928         }else{
10929             this.inputEl().dom.setAttribute('readOnly', false);
10930             this.inputEl().un('mousedown', this.onTriggerClick,  this);
10931             this.inputEl().removeClass('x-combo-noedit');
10932         }
10933     },
10934
10935     // private
10936     
10937     onBeforeLoad : function(combo,opts){
10938         if(!this.hasFocus){
10939             return;
10940         }
10941          if (!opts.add) {
10942             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
10943          }
10944 //        this.restrictHeight();
10945         this.selectedIndex = -1;
10946     },
10947
10948     // private
10949     onLoad : function(){
10950         
10951         this.hasQuery = false;
10952         
10953         if(!this.hasFocus){
10954             return;
10955         }
10956         
10957         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
10958             this.loading.hide();
10959         }
10960         
10961         if(this.store.getCount() > 0){
10962             this.expand();
10963 //            this.restrictHeight();
10964             if(this.lastQuery == this.allQuery){
10965                 if(this.editable && !this.tickable){
10966                     this.inputEl().dom.select();
10967                 }
10968                 if(!this.selectByValue(this.value, true) && this.autoFocus){
10969                     this.select(0, true);
10970                 }
10971             }else{
10972                 if(this.autoFocus){
10973                     this.selectNext();
10974                 }
10975                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
10976                     this.taTask.delay(this.typeAheadDelay);
10977                 }
10978             }
10979         }else{
10980             this.onEmptyResults();
10981         }
10982         
10983         //this.el.focus();
10984     },
10985     // private
10986     onLoadException : function()
10987     {
10988         this.hasQuery = false;
10989         
10990         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
10991             this.loading.hide();
10992         }
10993         
10994         this.collapse();
10995         Roo.log(this.store.reader.jsonData);
10996         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
10997             // fixme
10998             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
10999         }
11000         
11001         
11002     },
11003     // private
11004     onTypeAhead : function(){
11005         if(this.store.getCount() > 0){
11006             var r = this.store.getAt(0);
11007             var newValue = r.data[this.displayField];
11008             var len = newValue.length;
11009             var selStart = this.getRawValue().length;
11010             
11011             if(selStart != len){
11012                 this.setRawValue(newValue);
11013                 this.selectText(selStart, newValue.length);
11014             }
11015         }
11016     },
11017
11018     // private
11019     onSelect : function(record, index){
11020         
11021         if(this.fireEvent('beforeselect', this, record, index) !== false){
11022         
11023             this.setFromData(index > -1 ? record.data : false);
11024             
11025             this.collapse();
11026             this.fireEvent('select', this, record, index);
11027         }
11028     },
11029
11030     /**
11031      * Returns the currently selected field value or empty string if no value is set.
11032      * @return {String} value The selected value
11033      */
11034     getValue : function(){
11035         
11036         if(this.multiple){
11037             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
11038         }
11039         
11040         if(this.valueField){
11041             return typeof this.value != 'undefined' ? this.value : '';
11042         }else{
11043             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
11044         }
11045     },
11046
11047     /**
11048      * Clears any text/value currently set in the field
11049      */
11050     clearValue : function(){
11051         if(this.hiddenField){
11052             this.hiddenField.dom.value = '';
11053         }
11054         this.value = '';
11055         this.setRawValue('');
11056         this.lastSelectionText = '';
11057         
11058     },
11059
11060     /**
11061      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
11062      * will be displayed in the field.  If the value does not match the data value of an existing item,
11063      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
11064      * Otherwise the field will be blank (although the value will still be set).
11065      * @param {String} value The value to match
11066      */
11067     setValue : function(v){
11068         if(this.multiple){
11069             this.syncValue();
11070             return;
11071         }
11072         
11073         var text = v;
11074         if(this.valueField){
11075             var r = this.findRecord(this.valueField, v);
11076             if(r){
11077                 text = r.data[this.displayField];
11078             }else if(this.valueNotFoundText !== undefined){
11079                 text = this.valueNotFoundText;
11080             }
11081         }
11082         this.lastSelectionText = text;
11083         if(this.hiddenField){
11084             this.hiddenField.dom.value = v;
11085         }
11086         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
11087         this.value = v;
11088     },
11089     /**
11090      * @property {Object} the last set data for the element
11091      */
11092     
11093     lastData : false,
11094     /**
11095      * Sets the value of the field based on a object which is related to the record format for the store.
11096      * @param {Object} value the value to set as. or false on reset?
11097      */
11098     setFromData : function(o){
11099         
11100         if(this.multiple){
11101             this.addItem(o);
11102             return;
11103         }
11104             
11105         var dv = ''; // display value
11106         var vv = ''; // value value..
11107         this.lastData = o;
11108         if (this.displayField) {
11109             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11110         } else {
11111             // this is an error condition!!!
11112             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
11113         }
11114         
11115         if(this.valueField){
11116             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
11117         }
11118         
11119         if(this.hiddenField){
11120             this.hiddenField.dom.value = vv;
11121             
11122             this.lastSelectionText = dv;
11123             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11124             this.value = vv;
11125             return;
11126         }
11127         // no hidden field.. - we store the value in 'value', but still display
11128         // display field!!!!
11129         this.lastSelectionText = dv;
11130         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11131         this.value = vv;
11132         
11133         
11134     },
11135     // private
11136     reset : function(){
11137         // overridden so that last data is reset..
11138         this.setValue(this.originalValue);
11139         this.clearInvalid();
11140         this.lastData = false;
11141         if (this.view) {
11142             this.view.clearSelections();
11143         }
11144     },
11145     // private
11146     findRecord : function(prop, value){
11147         var record;
11148         if(this.store.getCount() > 0){
11149             this.store.each(function(r){
11150                 if(r.data[prop] == value){
11151                     record = r;
11152                     return false;
11153                 }
11154                 return true;
11155             });
11156         }
11157         return record;
11158     },
11159     
11160     getName: function()
11161     {
11162         // returns hidden if it's set..
11163         if (!this.rendered) {return ''};
11164         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
11165         
11166     },
11167     // private
11168     onViewMove : function(e, t){
11169         this.inKeyMode = false;
11170     },
11171
11172     // private
11173     onViewOver : function(e, t){
11174         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
11175             return;
11176         }
11177         var item = this.view.findItemFromChild(t);
11178         
11179         if(item){
11180             var index = this.view.indexOf(item);
11181             this.select(index, false);
11182         }
11183     },
11184
11185     // private
11186     onViewClick : function(view, doFocus, el, e)
11187     {
11188         var index = this.view.getSelectedIndexes()[0];
11189         
11190         var r = this.store.getAt(index);
11191         
11192         if(this.tickable){
11193             
11194             if(e.getTarget().nodeName.toLowerCase() != 'input'){
11195                 return;
11196             }
11197             
11198             var rm = false;
11199             var _this = this;
11200             
11201             Roo.each(this.tickItems, function(v,k){
11202                 
11203                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
11204                     _this.tickItems.splice(k, 1);
11205                     rm = true;
11206                     return;
11207                 }
11208             })
11209             
11210             if(rm){
11211                 return;
11212             }
11213             
11214             this.tickItems.push(r.data);
11215             return;
11216         }
11217         
11218         if(r){
11219             this.onSelect(r, index);
11220         }
11221         if(doFocus !== false && !this.blockFocus){
11222             this.inputEl().focus();
11223         }
11224     },
11225
11226     // private
11227     restrictHeight : function(){
11228         //this.innerList.dom.style.height = '';
11229         //var inner = this.innerList.dom;
11230         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
11231         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
11232         //this.list.beginUpdate();
11233         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
11234         this.list.alignTo(this.inputEl(), this.listAlign);
11235         this.list.alignTo(this.inputEl(), this.listAlign);
11236         //this.list.endUpdate();
11237     },
11238
11239     // private
11240     onEmptyResults : function(){
11241         this.collapse();
11242     },
11243
11244     /**
11245      * Returns true if the dropdown list is expanded, else false.
11246      */
11247     isExpanded : function(){
11248         return this.list.isVisible();
11249     },
11250
11251     /**
11252      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
11253      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11254      * @param {String} value The data value of the item to select
11255      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11256      * selected item if it is not currently in view (defaults to true)
11257      * @return {Boolean} True if the value matched an item in the list, else false
11258      */
11259     selectByValue : function(v, scrollIntoView){
11260         if(v !== undefined && v !== null){
11261             var r = this.findRecord(this.valueField || this.displayField, v);
11262             if(r){
11263                 this.select(this.store.indexOf(r), scrollIntoView);
11264                 return true;
11265             }
11266         }
11267         return false;
11268     },
11269
11270     /**
11271      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
11272      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11273      * @param {Number} index The zero-based index of the list item to select
11274      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11275      * selected item if it is not currently in view (defaults to true)
11276      */
11277     select : function(index, scrollIntoView){
11278         this.selectedIndex = index;
11279         this.view.select(index);
11280         if(scrollIntoView !== false){
11281             var el = this.view.getNode(index);
11282             if(el){
11283                 //this.innerList.scrollChildIntoView(el, false);
11284                 
11285             }
11286         }
11287     },
11288
11289     // private
11290     selectNext : function(){
11291         var ct = this.store.getCount();
11292         if(ct > 0){
11293             if(this.selectedIndex == -1){
11294                 this.select(0);
11295             }else if(this.selectedIndex < ct-1){
11296                 this.select(this.selectedIndex+1);
11297             }
11298         }
11299     },
11300
11301     // private
11302     selectPrev : function(){
11303         var ct = this.store.getCount();
11304         if(ct > 0){
11305             if(this.selectedIndex == -1){
11306                 this.select(0);
11307             }else if(this.selectedIndex != 0){
11308                 this.select(this.selectedIndex-1);
11309             }
11310         }
11311     },
11312
11313     // private
11314     onKeyUp : function(e){
11315         if(this.editable !== false && !e.isSpecialKey()){
11316             this.lastKey = e.getKey();
11317             this.dqTask.delay(this.queryDelay);
11318         }
11319     },
11320
11321     // private
11322     validateBlur : function(){
11323         return !this.list || !this.list.isVisible();   
11324     },
11325
11326     // private
11327     initQuery : function(){
11328         this.doQuery(this.getRawValue());
11329     },
11330
11331     // private
11332     doForce : function(){
11333         if(this.inputEl().dom.value.length > 0){
11334             this.inputEl().dom.value =
11335                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
11336              
11337         }
11338     },
11339
11340     /**
11341      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
11342      * query allowing the query action to be canceled if needed.
11343      * @param {String} query The SQL query to execute
11344      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
11345      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
11346      * saved in the current store (defaults to false)
11347      */
11348     doQuery : function(q, forceAll){
11349         
11350         if(q === undefined || q === null){
11351             q = '';
11352         }
11353         var qe = {
11354             query: q,
11355             forceAll: forceAll,
11356             combo: this,
11357             cancel:false
11358         };
11359         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
11360             return false;
11361         }
11362         q = qe.query;
11363         
11364         forceAll = qe.forceAll;
11365         if(forceAll === true || (q.length >= this.minChars)){
11366             
11367             this.hasQuery = true;
11368             
11369             if(this.lastQuery != q || this.alwaysQuery){
11370                 this.lastQuery = q;
11371                 if(this.mode == 'local'){
11372                     this.selectedIndex = -1;
11373                     if(forceAll){
11374                         this.store.clearFilter();
11375                     }else{
11376                         this.store.filter(this.displayField, q);
11377                     }
11378                     this.onLoad();
11379                 }else{
11380                     this.store.baseParams[this.queryParam] = q;
11381                     
11382                     var options = {params : this.getParams(q)};
11383                     
11384                     if(this.loadNext){
11385                         options.add = true;
11386                         options.params.start = this.page * this.pageSize;
11387                     }
11388                     
11389                     this.store.load(options);
11390                     /*
11391                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
11392                      *  we should expand the list on onLoad
11393                      *  so command out it
11394                      */
11395 //                    this.expand();
11396                 }
11397             }else{
11398                 this.selectedIndex = -1;
11399                 this.onLoad();   
11400             }
11401         }
11402         
11403         this.loadNext = false;
11404     },
11405
11406     // private
11407     getParams : function(q){
11408         var p = {};
11409         //p[this.queryParam] = q;
11410         
11411         if(this.pageSize){
11412             p.start = 0;
11413             p.limit = this.pageSize;
11414         }
11415         return p;
11416     },
11417
11418     /**
11419      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
11420      */
11421     collapse : function(){
11422         if(!this.isExpanded()){
11423             return;
11424         }
11425         
11426         this.hasFocus = false;
11427         
11428         this.list.hide();
11429         
11430         if(this.tickable){
11431             this.okBtn.hide();
11432             this.cancelBtn.hide();
11433             this.trigger.show();
11434         }
11435         
11436         Roo.get(document).un('mousedown', this.collapseIf, this);
11437         Roo.get(document).un('mousewheel', this.collapseIf, this);
11438         if (!this.editable) {
11439             Roo.get(document).un('keydown', this.listKeyPress, this);
11440         }
11441         this.fireEvent('collapse', this);
11442     },
11443
11444     // private
11445     collapseIf : function(e){
11446         var in_combo  = e.within(this.el);
11447         var in_list =  e.within(this.list);
11448         
11449         if (in_combo || in_list) {
11450             //e.stopPropagation();
11451             return;
11452         }
11453         
11454         if(this.tickable){
11455             this.onTickableFooterButtonClick(e, false, false);
11456         }
11457
11458         this.collapse();
11459         
11460     },
11461
11462     /**
11463      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
11464      */
11465     expand : function(){
11466        
11467         if(this.isExpanded() || !this.hasFocus){
11468             return;
11469         }
11470          Roo.log('expand');
11471         
11472         this.list.show();
11473         
11474         this.restrictHeight();
11475         
11476         if(this.tickable){
11477             
11478             this.tickItems = Roo.apply([], this.item);
11479             
11480             this.okBtn.show();
11481             this.cancelBtn.show();
11482             this.trigger.hide();
11483             
11484         }
11485         
11486         Roo.get(document).on('mousedown', this.collapseIf, this);
11487         Roo.get(document).on('mousewheel', this.collapseIf, this);
11488         if (!this.editable) {
11489             Roo.get(document).on('keydown', this.listKeyPress, this);
11490         }
11491         
11492         this.fireEvent('expand', this);
11493     },
11494
11495     // private
11496     // Implements the default empty TriggerField.onTriggerClick function
11497     onTriggerClick : function(e)
11498     {
11499         Roo.log('trigger click');
11500         
11501         if(this.disabled || !this.triggerList){
11502             return;
11503         }
11504         
11505         this.page = 0;
11506         this.loadNext = false;
11507         
11508         if(this.isExpanded()){
11509             this.collapse();
11510             if (!this.blockFocus) {
11511                 this.inputEl().focus();
11512             }
11513             
11514         }else {
11515             this.hasFocus = true;
11516             if(this.triggerAction == 'all') {
11517                 this.doQuery(this.allQuery, true);
11518             } else {
11519                 this.doQuery(this.getRawValue());
11520             }
11521             if (!this.blockFocus) {
11522                 this.inputEl().focus();
11523             }
11524         }
11525     },
11526     
11527     onTickableTriggerClick : function(e)
11528     {
11529         if(this.disabled){
11530             return;
11531         }
11532         
11533         this.page = 0;
11534         this.loadNext = false;
11535         this.hasFocus = true;
11536         
11537         if(this.triggerAction == 'all') {
11538             this.doQuery(this.allQuery, true);
11539         } else {
11540             this.doQuery(this.getRawValue());
11541         }
11542     },
11543     
11544     onSearchFieldClick : function(e)
11545     {
11546         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
11547             return;
11548         }
11549         
11550         this.page = 0;
11551         this.loadNext = false;
11552         this.hasFocus = true;
11553         
11554         if(this.triggerAction == 'all') {
11555             this.doQuery(this.allQuery, true);
11556         } else {
11557             this.doQuery(this.getRawValue());
11558         }
11559     },
11560     
11561     listKeyPress : function(e)
11562     {
11563         //Roo.log('listkeypress');
11564         // scroll to first matching element based on key pres..
11565         if (e.isSpecialKey()) {
11566             return false;
11567         }
11568         var k = String.fromCharCode(e.getKey()).toUpperCase();
11569         //Roo.log(k);
11570         var match  = false;
11571         var csel = this.view.getSelectedNodes();
11572         var cselitem = false;
11573         if (csel.length) {
11574             var ix = this.view.indexOf(csel[0]);
11575             cselitem  = this.store.getAt(ix);
11576             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
11577                 cselitem = false;
11578             }
11579             
11580         }
11581         
11582         this.store.each(function(v) { 
11583             if (cselitem) {
11584                 // start at existing selection.
11585                 if (cselitem.id == v.id) {
11586                     cselitem = false;
11587                 }
11588                 return true;
11589             }
11590                 
11591             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
11592                 match = this.store.indexOf(v);
11593                 return false;
11594             }
11595             return true;
11596         }, this);
11597         
11598         if (match === false) {
11599             return true; // no more action?
11600         }
11601         // scroll to?
11602         this.view.select(match);
11603         var sn = Roo.get(this.view.getSelectedNodes()[0])
11604         //sn.scrollIntoView(sn.dom.parentNode, false);
11605     },
11606     
11607     onViewScroll : function(e, t){
11608         
11609         if(this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
11610             return;
11611         }
11612         
11613         this.hasQuery = true;
11614         
11615         this.loading = this.list.select('.loading', true).first();
11616         
11617         if(this.loading === null){
11618             this.list.createChild({
11619                 tag: 'div',
11620                 cls: 'loading select2-more-results select2-active',
11621                 html: 'Loading more results...'
11622             })
11623             
11624             this.loading = this.list.select('.loading', true).first();
11625             
11626             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
11627             
11628             this.loading.hide();
11629         }
11630         
11631         this.loading.show();
11632         
11633         var _combo = this;
11634         
11635         this.page++;
11636         this.loadNext = true;
11637         
11638         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
11639         
11640         return;
11641     },
11642     
11643     addItem : function(o)
11644     {   
11645         var dv = ''; // display value
11646         
11647         if (this.displayField) {
11648             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11649         } else {
11650             // this is an error condition!!!
11651             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
11652         }
11653         
11654         if(!dv.length){
11655             return;
11656         }
11657         
11658         var choice = this.choices.createChild({
11659             tag: 'li',
11660             cls: 'select2-search-choice',
11661             cn: [
11662                 {
11663                     tag: 'div',
11664                     html: dv
11665                 },
11666                 {
11667                     tag: 'a',
11668                     href: '#',
11669                     cls: 'select2-search-choice-close',
11670                     tabindex: '-1'
11671                 }
11672             ]
11673             
11674         }, this.searchField);
11675         
11676         var close = choice.select('a.select2-search-choice-close', true).first()
11677         
11678         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
11679         
11680         this.item.push(o);
11681         
11682         this.lastData = o;
11683         
11684         this.syncValue();
11685         
11686         this.inputEl().dom.value = '';
11687         
11688     },
11689     
11690     onRemoveItem : function(e, _self, o)
11691     {
11692         e.preventDefault();
11693         var index = this.item.indexOf(o.data) * 1;
11694         
11695         if( index < 0){
11696             Roo.log('not this item?!');
11697             return;
11698         }
11699         
11700         this.item.splice(index, 1);
11701         o.item.remove();
11702         
11703         this.syncValue();
11704         
11705         this.fireEvent('remove', this, e);
11706         
11707     },
11708     
11709     syncValue : function()
11710     {
11711         if(!this.item.length){
11712             this.clearValue();
11713             return;
11714         }
11715             
11716         var value = [];
11717         var _this = this;
11718         Roo.each(this.item, function(i){
11719             if(_this.valueField){
11720                 value.push(i[_this.valueField]);
11721                 return;
11722             }
11723
11724             value.push(i);
11725         });
11726
11727         this.value = value.join(',');
11728
11729         if(this.hiddenField){
11730             this.hiddenField.dom.value = this.value;
11731         }
11732     },
11733     
11734     clearItem : function()
11735     {
11736         if(!this.multiple){
11737             return;
11738         }
11739         
11740         this.item = [];
11741         
11742         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
11743            c.remove();
11744         });
11745         
11746         this.syncValue();
11747     },
11748     
11749     inputEl: function ()
11750     {
11751         if(this.tickable){
11752             return this.searchField;
11753         }
11754         return this.el.select('input.form-control',true).first();
11755     },
11756     
11757     
11758     onTickableFooterButtonClick : function(e, btn, el)
11759     {
11760         e.preventDefault();
11761         
11762         if(btn && btn.name == 'cancel'){
11763             this.tickItems = Roo.apply([], this.item);
11764             this.collapse();
11765             return;
11766         }
11767         
11768         this.clearItem();
11769         
11770         var _this = this;
11771         
11772         Roo.each(this.tickItems, function(o){
11773             _this.addItem(o);
11774         });
11775         
11776         this.collapse();
11777         
11778     }
11779     
11780     
11781
11782     /** 
11783     * @cfg {Boolean} grow 
11784     * @hide 
11785     */
11786     /** 
11787     * @cfg {Number} growMin 
11788     * @hide 
11789     */
11790     /** 
11791     * @cfg {Number} growMax 
11792     * @hide 
11793     */
11794     /**
11795      * @hide
11796      * @method autoSize
11797      */
11798 });
11799 /*
11800  * Based on:
11801  * Ext JS Library 1.1.1
11802  * Copyright(c) 2006-2007, Ext JS, LLC.
11803  *
11804  * Originally Released Under LGPL - original licence link has changed is not relivant.
11805  *
11806  * Fork - LGPL
11807  * <script type="text/javascript">
11808  */
11809
11810 /**
11811  * @class Roo.View
11812  * @extends Roo.util.Observable
11813  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
11814  * This class also supports single and multi selection modes. <br>
11815  * Create a data model bound view:
11816  <pre><code>
11817  var store = new Roo.data.Store(...);
11818
11819  var view = new Roo.View({
11820     el : "my-element",
11821     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
11822  
11823     singleSelect: true,
11824     selectedClass: "ydataview-selected",
11825     store: store
11826  });
11827
11828  // listen for node click?
11829  view.on("click", function(vw, index, node, e){
11830  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
11831  });
11832
11833  // load XML data
11834  dataModel.load("foobar.xml");
11835  </code></pre>
11836  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
11837  * <br><br>
11838  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
11839  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
11840  * 
11841  * Note: old style constructor is still suported (container, template, config)
11842  * 
11843  * @constructor
11844  * Create a new View
11845  * @param {Object} config The config object
11846  * 
11847  */
11848 Roo.View = function(config, depreciated_tpl, depreciated_config){
11849     
11850     this.parent = false;
11851     
11852     if (typeof(depreciated_tpl) == 'undefined') {
11853         // new way.. - universal constructor.
11854         Roo.apply(this, config);
11855         this.el  = Roo.get(this.el);
11856     } else {
11857         // old format..
11858         this.el  = Roo.get(config);
11859         this.tpl = depreciated_tpl;
11860         Roo.apply(this, depreciated_config);
11861     }
11862     this.wrapEl  = this.el.wrap().wrap();
11863     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
11864     
11865     
11866     if(typeof(this.tpl) == "string"){
11867         this.tpl = new Roo.Template(this.tpl);
11868     } else {
11869         // support xtype ctors..
11870         this.tpl = new Roo.factory(this.tpl, Roo);
11871     }
11872     
11873     
11874     this.tpl.compile();
11875     
11876     /** @private */
11877     this.addEvents({
11878         /**
11879          * @event beforeclick
11880          * Fires before a click is processed. Returns false to cancel the default action.
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             "beforeclick" : true,
11887         /**
11888          * @event click
11889          * Fires when a template node is 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             "click" : true,
11896         /**
11897          * @event dblclick
11898          * Fires when a template node is double 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             "dblclick" : true,
11905         /**
11906          * @event contextmenu
11907          * Fires when a template node is right clicked.
11908          * @param {Roo.View} this
11909          * @param {Number} index The index of the target node
11910          * @param {HTMLElement} node The target node
11911          * @param {Roo.EventObject} e The raw event object
11912          */
11913             "contextmenu" : true,
11914         /**
11915          * @event selectionchange
11916          * Fires when the selected nodes change.
11917          * @param {Roo.View} this
11918          * @param {Array} selections Array of the selected nodes
11919          */
11920             "selectionchange" : true,
11921     
11922         /**
11923          * @event beforeselect
11924          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
11925          * @param {Roo.View} this
11926          * @param {HTMLElement} node The node to be selected
11927          * @param {Array} selections Array of currently selected nodes
11928          */
11929             "beforeselect" : true,
11930         /**
11931          * @event preparedata
11932          * Fires on every row to render, to allow you to change the data.
11933          * @param {Roo.View} this
11934          * @param {Object} data to be rendered (change this)
11935          */
11936           "preparedata" : true
11937           
11938           
11939         });
11940
11941
11942
11943     this.el.on({
11944         "click": this.onClick,
11945         "dblclick": this.onDblClick,
11946         "contextmenu": this.onContextMenu,
11947         scope:this
11948     });
11949
11950     this.selections = [];
11951     this.nodes = [];
11952     this.cmp = new Roo.CompositeElementLite([]);
11953     if(this.store){
11954         this.store = Roo.factory(this.store, Roo.data);
11955         this.setStore(this.store, true);
11956     }
11957     
11958     if ( this.footer && this.footer.xtype) {
11959            
11960          var fctr = this.wrapEl.appendChild(document.createElement("div"));
11961         
11962         this.footer.dataSource = this.store
11963         this.footer.container = fctr;
11964         this.footer = Roo.factory(this.footer, Roo);
11965         fctr.insertFirst(this.el);
11966         
11967         // this is a bit insane - as the paging toolbar seems to detach the el..
11968 //        dom.parentNode.parentNode.parentNode
11969          // they get detached?
11970     }
11971     
11972     
11973     Roo.View.superclass.constructor.call(this);
11974     
11975     
11976 };
11977
11978 Roo.extend(Roo.View, Roo.util.Observable, {
11979     
11980      /**
11981      * @cfg {Roo.data.Store} store Data store to load data from.
11982      */
11983     store : false,
11984     
11985     /**
11986      * @cfg {String|Roo.Element} el The container element.
11987      */
11988     el : '',
11989     
11990     /**
11991      * @cfg {String|Roo.Template} tpl The template used by this View 
11992      */
11993     tpl : false,
11994     /**
11995      * @cfg {String} dataName the named area of the template to use as the data area
11996      *                          Works with domtemplates roo-name="name"
11997      */
11998     dataName: false,
11999     /**
12000      * @cfg {String} selectedClass The css class to add to selected nodes
12001      */
12002     selectedClass : "x-view-selected",
12003      /**
12004      * @cfg {String} emptyText The empty text to show when nothing is loaded.
12005      */
12006     emptyText : "",
12007     
12008     /**
12009      * @cfg {String} text to display on mask (default Loading)
12010      */
12011     mask : false,
12012     /**
12013      * @cfg {Boolean} multiSelect Allow multiple selection
12014      */
12015     multiSelect : false,
12016     /**
12017      * @cfg {Boolean} singleSelect Allow single selection
12018      */
12019     singleSelect:  false,
12020     
12021     /**
12022      * @cfg {Boolean} toggleSelect - selecting 
12023      */
12024     toggleSelect : false,
12025     
12026     /**
12027      * @cfg {Boolean} tickable - selecting 
12028      */
12029     tickable : false,
12030     
12031     /**
12032      * Returns the element this view is bound to.
12033      * @return {Roo.Element}
12034      */
12035     getEl : function(){
12036         return this.wrapEl;
12037     },
12038     
12039     
12040
12041     /**
12042      * Refreshes the view. - called by datachanged on the store. - do not call directly.
12043      */
12044     refresh : function(){
12045         Roo.log('refresh');
12046         var t = this.tpl;
12047         
12048         // if we are using something like 'domtemplate', then
12049         // the what gets used is:
12050         // t.applySubtemplate(NAME, data, wrapping data..)
12051         // the outer template then get' applied with
12052         //     the store 'extra data'
12053         // and the body get's added to the
12054         //      roo-name="data" node?
12055         //      <span class='roo-tpl-{name}'></span> ?????
12056         
12057         
12058         
12059         this.clearSelections();
12060         this.el.update("");
12061         var html = [];
12062         var records = this.store.getRange();
12063         if(records.length < 1) {
12064             
12065             // is this valid??  = should it render a template??
12066             
12067             this.el.update(this.emptyText);
12068             return;
12069         }
12070         var el = this.el;
12071         if (this.dataName) {
12072             this.el.update(t.apply(this.store.meta)); //????
12073             el = this.el.child('.roo-tpl-' + this.dataName);
12074         }
12075         
12076         for(var i = 0, len = records.length; i < len; i++){
12077             var data = this.prepareData(records[i].data, i, records[i]);
12078             this.fireEvent("preparedata", this, data, i, records[i]);
12079             
12080             var d = Roo.apply({}, data);
12081             
12082             if(this.tickable){
12083                 Roo.apply(d, {'roo-id' : Roo.id()});
12084                 
12085                 var _this = this;
12086             
12087                 Roo.each(this.parent.item, function(item){
12088                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
12089                         return;
12090                     }
12091                     Roo.apply(d, {'roo-data-checked' : 'checked'});
12092                 });
12093             }
12094             
12095             html[html.length] = Roo.util.Format.trim(
12096                 this.dataName ?
12097                     t.applySubtemplate(this.dataName, d, this.store.meta) :
12098                     t.apply(d)
12099             );
12100         }
12101         
12102         
12103         
12104         el.update(html.join(""));
12105         this.nodes = el.dom.childNodes;
12106         this.updateIndexes(0);
12107     },
12108     
12109
12110     /**
12111      * Function to override to reformat the data that is sent to
12112      * the template for each node.
12113      * DEPRICATED - use the preparedata event handler.
12114      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
12115      * a JSON object for an UpdateManager bound view).
12116      */
12117     prepareData : function(data, index, record)
12118     {
12119         this.fireEvent("preparedata", this, data, index, record);
12120         return data;
12121     },
12122
12123     onUpdate : function(ds, record){
12124          Roo.log('on update');   
12125         this.clearSelections();
12126         var index = this.store.indexOf(record);
12127         var n = this.nodes[index];
12128         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
12129         n.parentNode.removeChild(n);
12130         this.updateIndexes(index, index);
12131     },
12132
12133     
12134     
12135 // --------- FIXME     
12136     onAdd : function(ds, records, index)
12137     {
12138         Roo.log(['on Add', ds, records, index] );        
12139         this.clearSelections();
12140         if(this.nodes.length == 0){
12141             this.refresh();
12142             return;
12143         }
12144         var n = this.nodes[index];
12145         for(var i = 0, len = records.length; i < len; i++){
12146             var d = this.prepareData(records[i].data, i, records[i]);
12147             if(n){
12148                 this.tpl.insertBefore(n, d);
12149             }else{
12150                 
12151                 this.tpl.append(this.el, d);
12152             }
12153         }
12154         this.updateIndexes(index);
12155     },
12156
12157     onRemove : function(ds, record, index){
12158         Roo.log('onRemove');
12159         this.clearSelections();
12160         var el = this.dataName  ?
12161             this.el.child('.roo-tpl-' + this.dataName) :
12162             this.el; 
12163         
12164         el.dom.removeChild(this.nodes[index]);
12165         this.updateIndexes(index);
12166     },
12167
12168     /**
12169      * Refresh an individual node.
12170      * @param {Number} index
12171      */
12172     refreshNode : function(index){
12173         this.onUpdate(this.store, this.store.getAt(index));
12174     },
12175
12176     updateIndexes : function(startIndex, endIndex){
12177         var ns = this.nodes;
12178         startIndex = startIndex || 0;
12179         endIndex = endIndex || ns.length - 1;
12180         for(var i = startIndex; i <= endIndex; i++){
12181             ns[i].nodeIndex = i;
12182         }
12183     },
12184
12185     /**
12186      * Changes the data store this view uses and refresh the view.
12187      * @param {Store} store
12188      */
12189     setStore : function(store, initial){
12190         if(!initial && this.store){
12191             this.store.un("datachanged", this.refresh);
12192             this.store.un("add", this.onAdd);
12193             this.store.un("remove", this.onRemove);
12194             this.store.un("update", this.onUpdate);
12195             this.store.un("clear", this.refresh);
12196             this.store.un("beforeload", this.onBeforeLoad);
12197             this.store.un("load", this.onLoad);
12198             this.store.un("loadexception", this.onLoad);
12199         }
12200         if(store){
12201           
12202             store.on("datachanged", this.refresh, this);
12203             store.on("add", this.onAdd, this);
12204             store.on("remove", this.onRemove, this);
12205             store.on("update", this.onUpdate, this);
12206             store.on("clear", this.refresh, this);
12207             store.on("beforeload", this.onBeforeLoad, this);
12208             store.on("load", this.onLoad, this);
12209             store.on("loadexception", this.onLoad, this);
12210         }
12211         
12212         if(store){
12213             this.refresh();
12214         }
12215     },
12216     /**
12217      * onbeforeLoad - masks the loading area.
12218      *
12219      */
12220     onBeforeLoad : function(store,opts)
12221     {
12222          Roo.log('onBeforeLoad');   
12223         if (!opts.add) {
12224             this.el.update("");
12225         }
12226         this.el.mask(this.mask ? this.mask : "Loading" ); 
12227     },
12228     onLoad : function ()
12229     {
12230         this.el.unmask();
12231     },
12232     
12233
12234     /**
12235      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
12236      * @param {HTMLElement} node
12237      * @return {HTMLElement} The template node
12238      */
12239     findItemFromChild : function(node){
12240         var el = this.dataName  ?
12241             this.el.child('.roo-tpl-' + this.dataName,true) :
12242             this.el.dom; 
12243         
12244         if(!node || node.parentNode == el){
12245                     return node;
12246             }
12247             var p = node.parentNode;
12248             while(p && p != el){
12249             if(p.parentNode == el){
12250                 return p;
12251             }
12252             p = p.parentNode;
12253         }
12254             return null;
12255     },
12256
12257     /** @ignore */
12258     onClick : function(e){
12259         var item = this.findItemFromChild(e.getTarget());
12260         if(item){
12261             var index = this.indexOf(item);
12262             if(this.onItemClick(item, index, e) !== false){
12263                 this.fireEvent("click", this, index, item, e);
12264             }
12265         }else{
12266             this.clearSelections();
12267         }
12268     },
12269
12270     /** @ignore */
12271     onContextMenu : function(e){
12272         var item = this.findItemFromChild(e.getTarget());
12273         if(item){
12274             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
12275         }
12276     },
12277
12278     /** @ignore */
12279     onDblClick : function(e){
12280         var item = this.findItemFromChild(e.getTarget());
12281         if(item){
12282             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
12283         }
12284     },
12285
12286     onItemClick : function(item, index, e)
12287     {
12288         if(this.fireEvent("beforeclick", this, index, item, e) === false){
12289             return false;
12290         }
12291         if (this.toggleSelect) {
12292             var m = this.isSelected(item) ? 'unselect' : 'select';
12293             Roo.log(m);
12294             var _t = this;
12295             _t[m](item, true, false);
12296             return true;
12297         }
12298         if(this.multiSelect || this.singleSelect){
12299             if(this.multiSelect && e.shiftKey && this.lastSelection){
12300                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
12301             }else{
12302                 this.select(item, this.multiSelect && e.ctrlKey);
12303                 this.lastSelection = item;
12304             }
12305             
12306             if(!this.tickable){
12307                 e.preventDefault();
12308             }
12309             
12310         }
12311         return true;
12312     },
12313
12314     /**
12315      * Get the number of selected nodes.
12316      * @return {Number}
12317      */
12318     getSelectionCount : function(){
12319         return this.selections.length;
12320     },
12321
12322     /**
12323      * Get the currently selected nodes.
12324      * @return {Array} An array of HTMLElements
12325      */
12326     getSelectedNodes : function(){
12327         return this.selections;
12328     },
12329
12330     /**
12331      * Get the indexes of the selected nodes.
12332      * @return {Array}
12333      */
12334     getSelectedIndexes : function(){
12335         var indexes = [], s = this.selections;
12336         for(var i = 0, len = s.length; i < len; i++){
12337             indexes.push(s[i].nodeIndex);
12338         }
12339         return indexes;
12340     },
12341
12342     /**
12343      * Clear all selections
12344      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
12345      */
12346     clearSelections : function(suppressEvent){
12347         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
12348             this.cmp.elements = this.selections;
12349             this.cmp.removeClass(this.selectedClass);
12350             this.selections = [];
12351             if(!suppressEvent){
12352                 this.fireEvent("selectionchange", this, this.selections);
12353             }
12354         }
12355     },
12356
12357     /**
12358      * Returns true if the passed node is selected
12359      * @param {HTMLElement/Number} node The node or node index
12360      * @return {Boolean}
12361      */
12362     isSelected : function(node){
12363         var s = this.selections;
12364         if(s.length < 1){
12365             return false;
12366         }
12367         node = this.getNode(node);
12368         return s.indexOf(node) !== -1;
12369     },
12370
12371     /**
12372      * Selects nodes.
12373      * @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
12374      * @param {Boolean} keepExisting (optional) true to keep existing selections
12375      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12376      */
12377     select : function(nodeInfo, keepExisting, suppressEvent){
12378         if(nodeInfo instanceof Array){
12379             if(!keepExisting){
12380                 this.clearSelections(true);
12381             }
12382             for(var i = 0, len = nodeInfo.length; i < len; i++){
12383                 this.select(nodeInfo[i], true, true);
12384             }
12385             return;
12386         } 
12387         var node = this.getNode(nodeInfo);
12388         if(!node || this.isSelected(node)){
12389             return; // already selected.
12390         }
12391         if(!keepExisting){
12392             this.clearSelections(true);
12393         }
12394         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
12395             Roo.fly(node).addClass(this.selectedClass);
12396             this.selections.push(node);
12397             if(!suppressEvent){
12398                 this.fireEvent("selectionchange", this, this.selections);
12399             }
12400         }
12401         
12402         
12403     },
12404       /**
12405      * Unselects nodes.
12406      * @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
12407      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
12408      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12409      */
12410     unselect : function(nodeInfo, keepExisting, suppressEvent)
12411     {
12412         if(nodeInfo instanceof Array){
12413             Roo.each(this.selections, function(s) {
12414                 this.unselect(s, nodeInfo);
12415             }, this);
12416             return;
12417         }
12418         var node = this.getNode(nodeInfo);
12419         if(!node || !this.isSelected(node)){
12420             Roo.log("not selected");
12421             return; // not selected.
12422         }
12423         // fireevent???
12424         var ns = [];
12425         Roo.each(this.selections, function(s) {
12426             if (s == node ) {
12427                 Roo.fly(node).removeClass(this.selectedClass);
12428
12429                 return;
12430             }
12431             ns.push(s);
12432         },this);
12433         
12434         this.selections= ns;
12435         this.fireEvent("selectionchange", this, this.selections);
12436     },
12437
12438     /**
12439      * Gets a template node.
12440      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12441      * @return {HTMLElement} The node or null if it wasn't found
12442      */
12443     getNode : function(nodeInfo){
12444         if(typeof nodeInfo == "string"){
12445             return document.getElementById(nodeInfo);
12446         }else if(typeof nodeInfo == "number"){
12447             return this.nodes[nodeInfo];
12448         }
12449         return nodeInfo;
12450     },
12451
12452     /**
12453      * Gets a range template nodes.
12454      * @param {Number} startIndex
12455      * @param {Number} endIndex
12456      * @return {Array} An array of nodes
12457      */
12458     getNodes : function(start, end){
12459         var ns = this.nodes;
12460         start = start || 0;
12461         end = typeof end == "undefined" ? ns.length - 1 : end;
12462         var nodes = [];
12463         if(start <= end){
12464             for(var i = start; i <= end; i++){
12465                 nodes.push(ns[i]);
12466             }
12467         } else{
12468             for(var i = start; i >= end; i--){
12469                 nodes.push(ns[i]);
12470             }
12471         }
12472         return nodes;
12473     },
12474
12475     /**
12476      * Finds the index of the passed node
12477      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12478      * @return {Number} The index of the node or -1
12479      */
12480     indexOf : function(node){
12481         node = this.getNode(node);
12482         if(typeof node.nodeIndex == "number"){
12483             return node.nodeIndex;
12484         }
12485         var ns = this.nodes;
12486         for(var i = 0, len = ns.length; i < len; i++){
12487             if(ns[i] == node){
12488                 return i;
12489             }
12490         }
12491         return -1;
12492     }
12493 });
12494 /*
12495  * - LGPL
12496  *
12497  * based on jquery fullcalendar
12498  * 
12499  */
12500
12501 Roo.bootstrap = Roo.bootstrap || {};
12502 /**
12503  * @class Roo.bootstrap.Calendar
12504  * @extends Roo.bootstrap.Component
12505  * Bootstrap Calendar class
12506  * @cfg {Boolean} loadMask (true|false) default false
12507  * @cfg {Object} header generate the user specific header of the calendar, default false
12508
12509  * @constructor
12510  * Create a new Container
12511  * @param {Object} config The config object
12512  */
12513
12514
12515
12516 Roo.bootstrap.Calendar = function(config){
12517     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
12518      this.addEvents({
12519         /**
12520              * @event select
12521              * Fires when a date is selected
12522              * @param {DatePicker} this
12523              * @param {Date} date The selected date
12524              */
12525         'select': true,
12526         /**
12527              * @event monthchange
12528              * Fires when the displayed month changes 
12529              * @param {DatePicker} this
12530              * @param {Date} date The selected month
12531              */
12532         'monthchange': true,
12533         /**
12534              * @event evententer
12535              * Fires when mouse over an event
12536              * @param {Calendar} this
12537              * @param {event} Event
12538              */
12539         'evententer': true,
12540         /**
12541              * @event eventleave
12542              * Fires when the mouse leaves an
12543              * @param {Calendar} this
12544              * @param {event}
12545              */
12546         'eventleave': true,
12547         /**
12548              * @event eventclick
12549              * Fires when the mouse click an
12550              * @param {Calendar} this
12551              * @param {event}
12552              */
12553         'eventclick': true
12554         
12555     });
12556
12557 };
12558
12559 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
12560     
12561      /**
12562      * @cfg {Number} startDay
12563      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
12564      */
12565     startDay : 0,
12566     
12567     loadMask : false,
12568     
12569     header : false,
12570       
12571     getAutoCreate : function(){
12572         
12573         
12574         var fc_button = function(name, corner, style, content ) {
12575             return Roo.apply({},{
12576                 tag : 'span',
12577                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
12578                          (corner.length ?
12579                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
12580                             ''
12581                         ),
12582                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
12583                 unselectable: 'on'
12584             });
12585         };
12586         
12587         var header = {};
12588         
12589         if(!this.header){
12590             header = {
12591                 tag : 'table',
12592                 cls : 'fc-header',
12593                 style : 'width:100%',
12594                 cn : [
12595                     {
12596                         tag: 'tr',
12597                         cn : [
12598                             {
12599                                 tag : 'td',
12600                                 cls : 'fc-header-left',
12601                                 cn : [
12602                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
12603                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
12604                                     { tag: 'span', cls: 'fc-header-space' },
12605                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
12606
12607
12608                                 ]
12609                             },
12610
12611                             {
12612                                 tag : 'td',
12613                                 cls : 'fc-header-center',
12614                                 cn : [
12615                                     {
12616                                         tag: 'span',
12617                                         cls: 'fc-header-title',
12618                                         cn : {
12619                                             tag: 'H2',
12620                                             html : 'month / year'
12621                                         }
12622                                     }
12623
12624                                 ]
12625                             },
12626                             {
12627                                 tag : 'td',
12628                                 cls : 'fc-header-right',
12629                                 cn : [
12630                               /*      fc_button('month', 'left', '', 'month' ),
12631                                     fc_button('week', '', '', 'week' ),
12632                                     fc_button('day', 'right', '', 'day' )
12633                                 */    
12634
12635                                 ]
12636                             }
12637
12638                         ]
12639                     }
12640                 ]
12641             };
12642         }
12643         
12644         header = this.header;
12645         
12646        
12647         var cal_heads = function() {
12648             var ret = [];
12649             // fixme - handle this.
12650             
12651             for (var i =0; i < Date.dayNames.length; i++) {
12652                 var d = Date.dayNames[i];
12653                 ret.push({
12654                     tag: 'th',
12655                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
12656                     html : d.substring(0,3)
12657                 });
12658                 
12659             }
12660             ret[0].cls += ' fc-first';
12661             ret[6].cls += ' fc-last';
12662             return ret;
12663         };
12664         var cal_cell = function(n) {
12665             return  {
12666                 tag: 'td',
12667                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
12668                 cn : [
12669                     {
12670                         cn : [
12671                             {
12672                                 cls: 'fc-day-number',
12673                                 html: 'D'
12674                             },
12675                             {
12676                                 cls: 'fc-day-content',
12677                              
12678                                 cn : [
12679                                      {
12680                                         style: 'position: relative;' // height: 17px;
12681                                     }
12682                                 ]
12683                             }
12684                             
12685                             
12686                         ]
12687                     }
12688                 ]
12689                 
12690             }
12691         };
12692         var cal_rows = function() {
12693             
12694             var ret = []
12695             for (var r = 0; r < 6; r++) {
12696                 var row= {
12697                     tag : 'tr',
12698                     cls : 'fc-week',
12699                     cn : []
12700                 };
12701                 
12702                 for (var i =0; i < Date.dayNames.length; i++) {
12703                     var d = Date.dayNames[i];
12704                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
12705
12706                 }
12707                 row.cn[0].cls+=' fc-first';
12708                 row.cn[0].cn[0].style = 'min-height:90px';
12709                 row.cn[6].cls+=' fc-last';
12710                 ret.push(row);
12711                 
12712             }
12713             ret[0].cls += ' fc-first';
12714             ret[4].cls += ' fc-prev-last';
12715             ret[5].cls += ' fc-last';
12716             return ret;
12717             
12718         };
12719         
12720         var cal_table = {
12721             tag: 'table',
12722             cls: 'fc-border-separate',
12723             style : 'width:100%',
12724             cellspacing  : 0,
12725             cn : [
12726                 { 
12727                     tag: 'thead',
12728                     cn : [
12729                         { 
12730                             tag: 'tr',
12731                             cls : 'fc-first fc-last',
12732                             cn : cal_heads()
12733                         }
12734                     ]
12735                 },
12736                 { 
12737                     tag: 'tbody',
12738                     cn : cal_rows()
12739                 }
12740                   
12741             ]
12742         };
12743          
12744          var cfg = {
12745             cls : 'fc fc-ltr',
12746             cn : [
12747                 header,
12748                 {
12749                     cls : 'fc-content',
12750                     style : "position: relative;",
12751                     cn : [
12752                         {
12753                             cls : 'fc-view fc-view-month fc-grid',
12754                             style : 'position: relative',
12755                             unselectable : 'on',
12756                             cn : [
12757                                 {
12758                                     cls : 'fc-event-container',
12759                                     style : 'position:absolute;z-index:8;top:0;left:0;'
12760                                 },
12761                                 cal_table
12762                             ]
12763                         }
12764                     ]
12765     
12766                 }
12767            ] 
12768             
12769         };
12770         
12771          
12772         
12773         return cfg;
12774     },
12775     
12776     
12777     initEvents : function()
12778     {
12779         if(!this.store){
12780             throw "can not find store for calendar";
12781         }
12782         
12783         var mark = {
12784             tag: "div",
12785             cls:"x-dlg-mask",
12786             style: "text-align:center",
12787             cn: [
12788                 {
12789                     tag: "div",
12790                     style: "background-color:white;width:50%;margin:250 auto",
12791                     cn: [
12792                         {
12793                             tag: "img",
12794                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
12795                         },
12796                         {
12797                             tag: "span",
12798                             html: "Loading"
12799                         }
12800                         
12801                     ]
12802                 }
12803             ]
12804         }
12805         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
12806         
12807         var size = this.el.select('.fc-content', true).first().getSize();
12808         this.maskEl.setSize(size.width, size.height);
12809         this.maskEl.enableDisplayMode("block");
12810         if(!this.loadMask){
12811             this.maskEl.hide();
12812         }
12813         
12814         this.store = Roo.factory(this.store, Roo.data);
12815         this.store.on('load', this.onLoad, this);
12816         this.store.on('beforeload', this.onBeforeLoad, this);
12817         
12818         this.resize();
12819         
12820         this.cells = this.el.select('.fc-day',true);
12821         //Roo.log(this.cells);
12822         this.textNodes = this.el.query('.fc-day-number');
12823         this.cells.addClassOnOver('fc-state-hover');
12824         
12825         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
12826         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
12827         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
12828         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
12829         
12830         this.on('monthchange', this.onMonthChange, this);
12831         
12832         this.update(new Date().clearTime());
12833     },
12834     
12835     resize : function() {
12836         var sz  = this.el.getSize();
12837         
12838         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
12839         this.el.select('.fc-day-content div',true).setHeight(34);
12840     },
12841     
12842     
12843     // private
12844     showPrevMonth : function(e){
12845         this.update(this.activeDate.add("mo", -1));
12846     },
12847     showToday : function(e){
12848         this.update(new Date().clearTime());
12849     },
12850     // private
12851     showNextMonth : function(e){
12852         this.update(this.activeDate.add("mo", 1));
12853     },
12854
12855     // private
12856     showPrevYear : function(){
12857         this.update(this.activeDate.add("y", -1));
12858     },
12859
12860     // private
12861     showNextYear : function(){
12862         this.update(this.activeDate.add("y", 1));
12863     },
12864
12865     
12866    // private
12867     update : function(date)
12868     {
12869         var vd = this.activeDate;
12870         this.activeDate = date;
12871 //        if(vd && this.el){
12872 //            var t = date.getTime();
12873 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
12874 //                Roo.log('using add remove');
12875 //                
12876 //                this.fireEvent('monthchange', this, date);
12877 //                
12878 //                this.cells.removeClass("fc-state-highlight");
12879 //                this.cells.each(function(c){
12880 //                   if(c.dateValue == t){
12881 //                       c.addClass("fc-state-highlight");
12882 //                       setTimeout(function(){
12883 //                            try{c.dom.firstChild.focus();}catch(e){}
12884 //                       }, 50);
12885 //                       return false;
12886 //                   }
12887 //                   return true;
12888 //                });
12889 //                return;
12890 //            }
12891 //        }
12892         
12893         var days = date.getDaysInMonth();
12894         
12895         var firstOfMonth = date.getFirstDateOfMonth();
12896         var startingPos = firstOfMonth.getDay()-this.startDay;
12897         
12898         if(startingPos < this.startDay){
12899             startingPos += 7;
12900         }
12901         
12902         var pm = date.add(Date.MONTH, -1);
12903         var prevStart = pm.getDaysInMonth()-startingPos;
12904 //        
12905         this.cells = this.el.select('.fc-day',true);
12906         this.textNodes = this.el.query('.fc-day-number');
12907         this.cells.addClassOnOver('fc-state-hover');
12908         
12909         var cells = this.cells.elements;
12910         var textEls = this.textNodes;
12911         
12912         Roo.each(cells, function(cell){
12913             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
12914         });
12915         
12916         days += startingPos;
12917
12918         // convert everything to numbers so it's fast
12919         var day = 86400000;
12920         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
12921         //Roo.log(d);
12922         //Roo.log(pm);
12923         //Roo.log(prevStart);
12924         
12925         var today = new Date().clearTime().getTime();
12926         var sel = date.clearTime().getTime();
12927         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
12928         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
12929         var ddMatch = this.disabledDatesRE;
12930         var ddText = this.disabledDatesText;
12931         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
12932         var ddaysText = this.disabledDaysText;
12933         var format = this.format;
12934         
12935         var setCellClass = function(cal, cell){
12936             cell.row = 0;
12937             cell.events = [];
12938             cell.more = [];
12939             //Roo.log('set Cell Class');
12940             cell.title = "";
12941             var t = d.getTime();
12942             
12943             //Roo.log(d);
12944             
12945             cell.dateValue = t;
12946             if(t == today){
12947                 cell.className += " fc-today";
12948                 cell.className += " fc-state-highlight";
12949                 cell.title = cal.todayText;
12950             }
12951             if(t == sel){
12952                 // disable highlight in other month..
12953                 //cell.className += " fc-state-highlight";
12954                 
12955             }
12956             // disabling
12957             if(t < min) {
12958                 cell.className = " fc-state-disabled";
12959                 cell.title = cal.minText;
12960                 return;
12961             }
12962             if(t > max) {
12963                 cell.className = " fc-state-disabled";
12964                 cell.title = cal.maxText;
12965                 return;
12966             }
12967             if(ddays){
12968                 if(ddays.indexOf(d.getDay()) != -1){
12969                     cell.title = ddaysText;
12970                     cell.className = " fc-state-disabled";
12971                 }
12972             }
12973             if(ddMatch && format){
12974                 var fvalue = d.dateFormat(format);
12975                 if(ddMatch.test(fvalue)){
12976                     cell.title = ddText.replace("%0", fvalue);
12977                     cell.className = " fc-state-disabled";
12978                 }
12979             }
12980             
12981             if (!cell.initialClassName) {
12982                 cell.initialClassName = cell.dom.className;
12983             }
12984             
12985             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
12986         };
12987
12988         var i = 0;
12989         
12990         for(; i < startingPos; i++) {
12991             textEls[i].innerHTML = (++prevStart);
12992             d.setDate(d.getDate()+1);
12993             
12994             cells[i].className = "fc-past fc-other-month";
12995             setCellClass(this, cells[i]);
12996         }
12997         
12998         var intDay = 0;
12999         
13000         for(; i < days; i++){
13001             intDay = i - startingPos + 1;
13002             textEls[i].innerHTML = (intDay);
13003             d.setDate(d.getDate()+1);
13004             
13005             cells[i].className = ''; // "x-date-active";
13006             setCellClass(this, cells[i]);
13007         }
13008         var extraDays = 0;
13009         
13010         for(; i < 42; i++) {
13011             textEls[i].innerHTML = (++extraDays);
13012             d.setDate(d.getDate()+1);
13013             
13014             cells[i].className = "fc-future fc-other-month";
13015             setCellClass(this, cells[i]);
13016         }
13017         
13018         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
13019         
13020         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
13021         
13022         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
13023         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
13024         
13025         if(totalRows != 6){
13026             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
13027             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
13028         }
13029         
13030         this.fireEvent('monthchange', this, date);
13031         
13032         
13033         /*
13034         if(!this.internalRender){
13035             var main = this.el.dom.firstChild;
13036             var w = main.offsetWidth;
13037             this.el.setWidth(w + this.el.getBorderWidth("lr"));
13038             Roo.fly(main).setWidth(w);
13039             this.internalRender = true;
13040             // opera does not respect the auto grow header center column
13041             // then, after it gets a width opera refuses to recalculate
13042             // without a second pass
13043             if(Roo.isOpera && !this.secondPass){
13044                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
13045                 this.secondPass = true;
13046                 this.update.defer(10, this, [date]);
13047             }
13048         }
13049         */
13050         
13051     },
13052     
13053     findCell : function(dt) {
13054         dt = dt.clearTime().getTime();
13055         var ret = false;
13056         this.cells.each(function(c){
13057             //Roo.log("check " +c.dateValue + '?=' + dt);
13058             if(c.dateValue == dt){
13059                 ret = c;
13060                 return false;
13061             }
13062             return true;
13063         });
13064         
13065         return ret;
13066     },
13067     
13068     findCells : function(ev) {
13069         var s = ev.start.clone().clearTime().getTime();
13070        // Roo.log(s);
13071         var e= ev.end.clone().clearTime().getTime();
13072        // Roo.log(e);
13073         var ret = [];
13074         this.cells.each(function(c){
13075              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
13076             
13077             if(c.dateValue > e){
13078                 return ;
13079             }
13080             if(c.dateValue < s){
13081                 return ;
13082             }
13083             ret.push(c);
13084         });
13085         
13086         return ret;    
13087     },
13088     
13089 //    findBestRow: function(cells)
13090 //    {
13091 //        var ret = 0;
13092 //        
13093 //        for (var i =0 ; i < cells.length;i++) {
13094 //            ret  = Math.max(cells[i].rows || 0,ret);
13095 //        }
13096 //        return ret;
13097 //        
13098 //    },
13099     
13100     
13101     addItem : function(ev)
13102     {
13103         // look for vertical location slot in
13104         var cells = this.findCells(ev);
13105         
13106 //        ev.row = this.findBestRow(cells);
13107         
13108         // work out the location.
13109         
13110         var crow = false;
13111         var rows = [];
13112         for(var i =0; i < cells.length; i++) {
13113             
13114             cells[i].row = cells[0].row;
13115             
13116             if(i == 0){
13117                 cells[i].row = cells[i].row + 1;
13118             }
13119             
13120             if (!crow) {
13121                 crow = {
13122                     start : cells[i],
13123                     end :  cells[i]
13124                 };
13125                 continue;
13126             }
13127             if (crow.start.getY() == cells[i].getY()) {
13128                 // on same row.
13129                 crow.end = cells[i];
13130                 continue;
13131             }
13132             // different row.
13133             rows.push(crow);
13134             crow = {
13135                 start: cells[i],
13136                 end : cells[i]
13137             };
13138             
13139         }
13140         
13141         rows.push(crow);
13142         ev.els = [];
13143         ev.rows = rows;
13144         ev.cells = cells;
13145         
13146         cells[0].events.push(ev);
13147         
13148         this.calevents.push(ev);
13149     },
13150     
13151     clearEvents: function() {
13152         
13153         if(!this.calevents){
13154             return;
13155         }
13156         
13157         Roo.each(this.cells.elements, function(c){
13158             c.row = 0;
13159             c.events = [];
13160             c.more = [];
13161         });
13162         
13163         Roo.each(this.calevents, function(e) {
13164             Roo.each(e.els, function(el) {
13165                 el.un('mouseenter' ,this.onEventEnter, this);
13166                 el.un('mouseleave' ,this.onEventLeave, this);
13167                 el.remove();
13168             },this);
13169         },this);
13170         
13171         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
13172             e.remove();
13173         });
13174         
13175     },
13176     
13177     renderEvents: function()
13178     {   
13179         var _this = this;
13180         
13181         this.cells.each(function(c) {
13182             
13183             if(c.row < 5){
13184                 return;
13185             }
13186             
13187             var ev = c.events;
13188             
13189             var r = 4;
13190             if(c.row != c.events.length){
13191                 r = 4 - (4 - (c.row - c.events.length));
13192             }
13193             
13194             c.events = ev.slice(0, r);
13195             c.more = ev.slice(r);
13196             
13197             if(c.more.length && c.more.length == 1){
13198                 c.events.push(c.more.pop());
13199             }
13200             
13201             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
13202             
13203         });
13204             
13205         this.cells.each(function(c) {
13206             
13207             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
13208             
13209             
13210             for (var e = 0; e < c.events.length; e++){
13211                 var ev = c.events[e];
13212                 var rows = ev.rows;
13213                 
13214                 for(var i = 0; i < rows.length; i++) {
13215                 
13216                     // how many rows should it span..
13217
13218                     var  cfg = {
13219                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
13220                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
13221
13222                         unselectable : "on",
13223                         cn : [
13224                             {
13225                                 cls: 'fc-event-inner',
13226                                 cn : [
13227     //                                {
13228     //                                  tag:'span',
13229     //                                  cls: 'fc-event-time',
13230     //                                  html : cells.length > 1 ? '' : ev.time
13231     //                                },
13232                                     {
13233                                       tag:'span',
13234                                       cls: 'fc-event-title',
13235                                       html : String.format('{0}', ev.title)
13236                                     }
13237
13238
13239                                 ]
13240                             },
13241                             {
13242                                 cls: 'ui-resizable-handle ui-resizable-e',
13243                                 html : '&nbsp;&nbsp;&nbsp'
13244                             }
13245
13246                         ]
13247                     };
13248
13249                     if (i == 0) {
13250                         cfg.cls += ' fc-event-start';
13251                     }
13252                     if ((i+1) == rows.length) {
13253                         cfg.cls += ' fc-event-end';
13254                     }
13255
13256                     var ctr = _this.el.select('.fc-event-container',true).first();
13257                     var cg = ctr.createChild(cfg);
13258
13259                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
13260                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
13261
13262                     var r = (c.more.length) ? 1 : 0;
13263                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
13264                     cg.setWidth(ebox.right - sbox.x -2);
13265
13266                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
13267                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
13268                     cg.on('click', _this.onEventClick, _this, ev);
13269
13270                     ev.els.push(cg);
13271                     
13272                 }
13273                 
13274             }
13275             
13276             
13277             if(c.more.length){
13278                 var  cfg = {
13279                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
13280                     style : 'position: absolute',
13281                     unselectable : "on",
13282                     cn : [
13283                         {
13284                             cls: 'fc-event-inner',
13285                             cn : [
13286                                 {
13287                                   tag:'span',
13288                                   cls: 'fc-event-title',
13289                                   html : 'More'
13290                                 }
13291
13292
13293                             ]
13294                         },
13295                         {
13296                             cls: 'ui-resizable-handle ui-resizable-e',
13297                             html : '&nbsp;&nbsp;&nbsp'
13298                         }
13299
13300                     ]
13301                 };
13302
13303                 var ctr = _this.el.select('.fc-event-container',true).first();
13304                 var cg = ctr.createChild(cfg);
13305
13306                 var sbox = c.select('.fc-day-content',true).first().getBox();
13307                 var ebox = c.select('.fc-day-content',true).first().getBox();
13308                 //Roo.log(cg);
13309                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
13310                 cg.setWidth(ebox.right - sbox.x -2);
13311
13312                 cg.on('click', _this.onMoreEventClick, _this, c.more);
13313                 
13314             }
13315             
13316         });
13317         
13318         
13319         
13320     },
13321     
13322     onEventEnter: function (e, el,event,d) {
13323         this.fireEvent('evententer', this, el, event);
13324     },
13325     
13326     onEventLeave: function (e, el,event,d) {
13327         this.fireEvent('eventleave', this, el, event);
13328     },
13329     
13330     onEventClick: function (e, el,event,d) {
13331         this.fireEvent('eventclick', this, el, event);
13332     },
13333     
13334     onMonthChange: function () {
13335         this.store.load();
13336     },
13337     
13338     onMoreEventClick: function(e, el, more)
13339     {
13340         var _this = this;
13341         
13342         this.calpopover.placement = 'right';
13343         this.calpopover.setTitle('More');
13344         
13345         this.calpopover.setContent('');
13346         
13347         var ctr = this.calpopover.el.select('.popover-content', true).first();
13348         
13349         Roo.each(more, function(m){
13350             var cfg = {
13351                 cls : 'fc-event-hori fc-event-draggable',
13352                 html : m.title
13353             }
13354             var cg = ctr.createChild(cfg);
13355             
13356             cg.on('click', _this.onEventClick, _this, m);
13357         });
13358         
13359         this.calpopover.show(el);
13360         
13361         
13362     },
13363     
13364     onLoad: function () 
13365     {   
13366         this.calevents = [];
13367         var cal = this;
13368         
13369         if(this.store.getCount() > 0){
13370             this.store.data.each(function(d){
13371                cal.addItem({
13372                     id : d.data.id,
13373                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
13374                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
13375                     time : d.data.start_time,
13376                     title : d.data.title,
13377                     description : d.data.description,
13378                     venue : d.data.venue
13379                 });
13380             });
13381         }
13382         
13383         this.renderEvents();
13384         
13385         if(this.calevents.length && this.loadMask){
13386             this.maskEl.hide();
13387         }
13388     },
13389     
13390     onBeforeLoad: function()
13391     {
13392         this.clearEvents();
13393         if(this.loadMask){
13394             this.maskEl.show();
13395         }
13396     }
13397 });
13398
13399  
13400  /*
13401  * - LGPL
13402  *
13403  * element
13404  * 
13405  */
13406
13407 /**
13408  * @class Roo.bootstrap.Popover
13409  * @extends Roo.bootstrap.Component
13410  * Bootstrap Popover class
13411  * @cfg {String} html contents of the popover   (or false to use children..)
13412  * @cfg {String} title of popover (or false to hide)
13413  * @cfg {String} placement how it is placed
13414  * @cfg {String} trigger click || hover (or false to trigger manually)
13415  * @cfg {String} over what (parent or false to trigger manually.)
13416  * 
13417  * @constructor
13418  * Create a new Popover
13419  * @param {Object} config The config object
13420  */
13421
13422 Roo.bootstrap.Popover = function(config){
13423     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
13424 };
13425
13426 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
13427     
13428     title: 'Fill in a title',
13429     html: false,
13430     
13431     placement : 'right',
13432     trigger : 'hover', // hover
13433     
13434     over: 'parent',
13435     
13436     can_build_overlaid : false,
13437     
13438     getChildContainer : function()
13439     {
13440         return this.el.select('.popover-content',true).first();
13441     },
13442     
13443     getAutoCreate : function(){
13444          Roo.log('make popover?');
13445         var cfg = {
13446            cls : 'popover roo-dynamic',
13447            style: 'display:block',
13448            cn : [
13449                 {
13450                     cls : 'arrow'
13451                 },
13452                 {
13453                     cls : 'popover-inner',
13454                     cn : [
13455                         {
13456                             tag: 'h3',
13457                             cls: 'popover-title',
13458                             html : this.title
13459                         },
13460                         {
13461                             cls : 'popover-content',
13462                             html : this.html
13463                         }
13464                     ]
13465                     
13466                 }
13467            ]
13468         };
13469         
13470         return cfg;
13471     },
13472     setTitle: function(str)
13473     {
13474         this.el.select('.popover-title',true).first().dom.innerHTML = str;
13475     },
13476     setContent: function(str)
13477     {
13478         this.el.select('.popover-content',true).first().dom.innerHTML = str;
13479     },
13480     // as it get's added to the bottom of the page.
13481     onRender : function(ct, position)
13482     {
13483         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
13484         if(!this.el){
13485             var cfg = Roo.apply({},  this.getAutoCreate());
13486             cfg.id = Roo.id();
13487             
13488             if (this.cls) {
13489                 cfg.cls += ' ' + this.cls;
13490             }
13491             if (this.style) {
13492                 cfg.style = this.style;
13493             }
13494             Roo.log("adding to ")
13495             this.el = Roo.get(document.body).createChild(cfg, position);
13496             Roo.log(this.el);
13497         }
13498         this.initEvents();
13499     },
13500     
13501     initEvents : function()
13502     {
13503         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
13504         this.el.enableDisplayMode('block');
13505         this.el.hide();
13506         if (this.over === false) {
13507             return; 
13508         }
13509         if (this.triggers === false) {
13510             return;
13511         }
13512         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13513         var triggers = this.trigger ? this.trigger.split(' ') : [];
13514         Roo.each(triggers, function(trigger) {
13515         
13516             if (trigger == 'click') {
13517                 on_el.on('click', this.toggle, this);
13518             } else if (trigger != 'manual') {
13519                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
13520                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
13521       
13522                 on_el.on(eventIn  ,this.enter, this);
13523                 on_el.on(eventOut, this.leave, this);
13524             }
13525         }, this);
13526         
13527     },
13528     
13529     
13530     // private
13531     timeout : null,
13532     hoverState : null,
13533     
13534     toggle : function () {
13535         this.hoverState == 'in' ? this.leave() : this.enter();
13536     },
13537     
13538     enter : function () {
13539        
13540     
13541         clearTimeout(this.timeout);
13542     
13543         this.hoverState = 'in'
13544     
13545         if (!this.delay || !this.delay.show) {
13546             this.show();
13547             return 
13548         }
13549         var _t = this;
13550         this.timeout = setTimeout(function () {
13551             if (_t.hoverState == 'in') {
13552                 _t.show();
13553             }
13554         }, this.delay.show)
13555     },
13556     leave : function() {
13557         clearTimeout(this.timeout);
13558     
13559         this.hoverState = 'out'
13560     
13561         if (!this.delay || !this.delay.hide) {
13562             this.hide();
13563             return 
13564         }
13565         var _t = this;
13566         this.timeout = setTimeout(function () {
13567             if (_t.hoverState == 'out') {
13568                 _t.hide();
13569             }
13570         }, this.delay.hide)
13571     },
13572     
13573     show : function (on_el)
13574     {
13575         if (!on_el) {
13576             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13577         }
13578         // set content.
13579         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
13580         if (this.html !== false) {
13581             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
13582         }
13583         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
13584         if (!this.title.length) {
13585             this.el.select('.popover-title',true).hide();
13586         }
13587         
13588         var placement = typeof this.placement == 'function' ?
13589             this.placement.call(this, this.el, on_el) :
13590             this.placement;
13591             
13592         var autoToken = /\s?auto?\s?/i;
13593         var autoPlace = autoToken.test(placement);
13594         if (autoPlace) {
13595             placement = placement.replace(autoToken, '') || 'top';
13596         }
13597         
13598         //this.el.detach()
13599         //this.el.setXY([0,0]);
13600         this.el.show();
13601         this.el.dom.style.display='block';
13602         this.el.addClass(placement);
13603         
13604         //this.el.appendTo(on_el);
13605         
13606         var p = this.getPosition();
13607         var box = this.el.getBox();
13608         
13609         if (autoPlace) {
13610             // fixme..
13611         }
13612         var align = Roo.bootstrap.Popover.alignment[placement]
13613         this.el.alignTo(on_el, align[0],align[1]);
13614         //var arrow = this.el.select('.arrow',true).first();
13615         //arrow.set(align[2], 
13616         
13617         this.el.addClass('in');
13618         this.hoverState = null;
13619         
13620         if (this.el.hasClass('fade')) {
13621             // fade it?
13622         }
13623         
13624     },
13625     hide : function()
13626     {
13627         this.el.setXY([0,0]);
13628         this.el.removeClass('in');
13629         this.el.hide();
13630         
13631     }
13632     
13633 });
13634
13635 Roo.bootstrap.Popover.alignment = {
13636     'left' : ['r-l', [-10,0], 'right'],
13637     'right' : ['l-r', [10,0], 'left'],
13638     'bottom' : ['t-b', [0,10], 'top'],
13639     'top' : [ 'b-t', [0,-10], 'bottom']
13640 };
13641
13642  /*
13643  * - LGPL
13644  *
13645  * Progress
13646  * 
13647  */
13648
13649 /**
13650  * @class Roo.bootstrap.Progress
13651  * @extends Roo.bootstrap.Component
13652  * Bootstrap Progress class
13653  * @cfg {Boolean} striped striped of the progress bar
13654  * @cfg {Boolean} active animated of the progress bar
13655  * 
13656  * 
13657  * @constructor
13658  * Create a new Progress
13659  * @param {Object} config The config object
13660  */
13661
13662 Roo.bootstrap.Progress = function(config){
13663     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
13664 };
13665
13666 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
13667     
13668     striped : false,
13669     active: false,
13670     
13671     getAutoCreate : function(){
13672         var cfg = {
13673             tag: 'div',
13674             cls: 'progress'
13675         };
13676         
13677         
13678         if(this.striped){
13679             cfg.cls += ' progress-striped';
13680         }
13681       
13682         if(this.active){
13683             cfg.cls += ' active';
13684         }
13685         
13686         
13687         return cfg;
13688     }
13689    
13690 });
13691
13692  
13693
13694  /*
13695  * - LGPL
13696  *
13697  * ProgressBar
13698  * 
13699  */
13700
13701 /**
13702  * @class Roo.bootstrap.ProgressBar
13703  * @extends Roo.bootstrap.Component
13704  * Bootstrap ProgressBar class
13705  * @cfg {Number} aria_valuenow aria-value now
13706  * @cfg {Number} aria_valuemin aria-value min
13707  * @cfg {Number} aria_valuemax aria-value max
13708  * @cfg {String} label label for the progress bar
13709  * @cfg {String} panel (success | info | warning | danger )
13710  * @cfg {String} role role of the progress bar
13711  * @cfg {String} sr_only text
13712  * 
13713  * 
13714  * @constructor
13715  * Create a new ProgressBar
13716  * @param {Object} config The config object
13717  */
13718
13719 Roo.bootstrap.ProgressBar = function(config){
13720     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
13721 };
13722
13723 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
13724     
13725     aria_valuenow : 0,
13726     aria_valuemin : 0,
13727     aria_valuemax : 100,
13728     label : false,
13729     panel : false,
13730     role : false,
13731     sr_only: false,
13732     
13733     getAutoCreate : function()
13734     {
13735         
13736         var cfg = {
13737             tag: 'div',
13738             cls: 'progress-bar',
13739             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
13740         };
13741         
13742         if(this.sr_only){
13743             cfg.cn = {
13744                 tag: 'span',
13745                 cls: 'sr-only',
13746                 html: this.sr_only
13747             }
13748         }
13749         
13750         if(this.role){
13751             cfg.role = this.role;
13752         }
13753         
13754         if(this.aria_valuenow){
13755             cfg['aria-valuenow'] = this.aria_valuenow;
13756         }
13757         
13758         if(this.aria_valuemin){
13759             cfg['aria-valuemin'] = this.aria_valuemin;
13760         }
13761         
13762         if(this.aria_valuemax){
13763             cfg['aria-valuemax'] = this.aria_valuemax;
13764         }
13765         
13766         if(this.label && !this.sr_only){
13767             cfg.html = this.label;
13768         }
13769         
13770         if(this.panel){
13771             cfg.cls += ' progress-bar-' + this.panel;
13772         }
13773         
13774         return cfg;
13775     },
13776     
13777     update : function(aria_valuenow)
13778     {
13779         this.aria_valuenow = aria_valuenow;
13780         
13781         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
13782     }
13783    
13784 });
13785
13786  
13787
13788  /*
13789  * - LGPL
13790  *
13791  * column
13792  * 
13793  */
13794
13795 /**
13796  * @class Roo.bootstrap.TabGroup
13797  * @extends Roo.bootstrap.Column
13798  * Bootstrap Column class
13799  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
13800  * @cfg {Boolean} carousel true to make the group behave like a carousel
13801  * 
13802  * @constructor
13803  * Create a new TabGroup
13804  * @param {Object} config The config object
13805  */
13806
13807 Roo.bootstrap.TabGroup = function(config){
13808     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
13809     if (!this.navId) {
13810         this.navId = Roo.id();
13811     }
13812     this.tabs = [];
13813     Roo.bootstrap.TabGroup.register(this);
13814     
13815 };
13816
13817 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
13818     
13819     carousel : false,
13820      
13821     getAutoCreate : function()
13822     {
13823         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
13824         
13825         cfg.cls += ' tab-content';
13826         
13827         if (this.carousel) {
13828             cfg.cls += ' carousel slide';
13829             cfg.cn = [{
13830                cls : 'carousel-inner'
13831             }]
13832         }
13833         
13834         
13835         return cfg;
13836     },
13837     getChildContainer : function()
13838     {
13839         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
13840     },
13841     
13842     /**
13843     * register a Navigation item
13844     * @param {Roo.bootstrap.NavItem} the navitem to add
13845     */
13846     register : function(item)
13847     {
13848         this.tabs.push( item);
13849         item.navId = this.navId; // not really needed..
13850     
13851     },
13852     
13853     getActivePanel : function()
13854     {
13855         var r = false;
13856         Roo.each(this.tabs, function(t) {
13857             if (t.active) {
13858                 r = t;
13859                 return false;
13860             }
13861             return null;
13862         });
13863         return r;
13864         
13865     },
13866     getPanelByName : function(n)
13867     {
13868         var r = false;
13869         Roo.each(this.tabs, function(t) {
13870             if (t.tabId == n) {
13871                 r = t;
13872                 return false;
13873             }
13874             return null;
13875         });
13876         return r;
13877     },
13878     indexOfPanel : function(p)
13879     {
13880         var r = false;
13881         Roo.each(this.tabs, function(t,i) {
13882             if (t.tabId == p.tabId) {
13883                 r = i;
13884                 return false;
13885             }
13886             return null;
13887         });
13888         return r;
13889     },
13890     /**
13891      * show a specific panel
13892      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
13893      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
13894      */
13895     showPanel : function (pan)
13896     {
13897         
13898         
13899         
13900         if (typeof(pan) == 'number') {
13901             pan = this.tabs[pan];
13902         }
13903         if (typeof(pan) == 'string') {
13904             pan = this.getPanelByName(pan);
13905         }
13906         if (pan.tabId == this.getActivePanel().tabId) {
13907             return true;
13908         }
13909         var cur = this.getActivePanel();
13910         
13911         if (false === cur.fireEvent('beforedeactivate')) {
13912             return false;
13913         }
13914         
13915         
13916         
13917         if (this.carousel) {
13918             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
13919             var lr = dir == 'next' ? 'left' : 'right';
13920             pan.el.addClass(dir); // or prev
13921             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
13922             cur.el.addClass(lr); // or right
13923             pan.el.addClass(lr);
13924             cur.el.on('transitionend', function() {
13925                 Roo.log("trans end?");
13926                 
13927                 pan.el.removeClass([lr,dir]);
13928                 pan.setActive(true);
13929                 
13930                 cur.el.removeClass([lr]);
13931                 cur.setActive(false);
13932                 
13933                 
13934             }, this, { single:  true } );
13935             return true;
13936         }
13937         
13938         cur.setActive(false);
13939         pan.setActive(true);
13940         return true;
13941         
13942     },
13943     showPanelNext : function()
13944     {
13945         var i = this.indexOfPanel(this.getActivePanel());
13946         if (i > this.tabs.length) {
13947             return;
13948         }
13949         this.showPanel(this.tabs[i+1]);
13950     },
13951     showPanelPrev : function()
13952     {
13953         var i = this.indexOfPanel(this.getActivePanel());
13954         if (i  < 1) {
13955             return;
13956         }
13957         this.showPanel(this.tabs[i-1]);
13958     }
13959     
13960     
13961   
13962 });
13963
13964  
13965
13966  
13967  
13968 Roo.apply(Roo.bootstrap.TabGroup, {
13969     
13970     groups: {},
13971      /**
13972     * register a Navigation Group
13973     * @param {Roo.bootstrap.NavGroup} the navgroup to add
13974     */
13975     register : function(navgrp)
13976     {
13977         this.groups[navgrp.navId] = navgrp;
13978         
13979     },
13980     /**
13981     * fetch a Navigation Group based on the navigation ID
13982     * if one does not exist , it will get created.
13983     * @param {string} the navgroup to add
13984     * @returns {Roo.bootstrap.NavGroup} the navgroup 
13985     */
13986     get: function(navId) {
13987         if (typeof(this.groups[navId]) == 'undefined') {
13988             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
13989         }
13990         return this.groups[navId] ;
13991     }
13992     
13993     
13994     
13995 });
13996
13997  /*
13998  * - LGPL
13999  *
14000  * TabPanel
14001  * 
14002  */
14003
14004 /**
14005  * @class Roo.bootstrap.TabPanel
14006  * @extends Roo.bootstrap.Component
14007  * Bootstrap TabPanel class
14008  * @cfg {Boolean} active panel active
14009  * @cfg {String} html panel content
14010  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
14011  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
14012  * 
14013  * 
14014  * @constructor
14015  * Create a new TabPanel
14016  * @param {Object} config The config object
14017  */
14018
14019 Roo.bootstrap.TabPanel = function(config){
14020     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
14021     this.addEvents({
14022         /**
14023              * @event changed
14024              * Fires when the active status changes
14025              * @param {Roo.bootstrap.TabPanel} this
14026              * @param {Boolean} state the new state
14027             
14028          */
14029         'changed': true,
14030         /**
14031              * @event beforedeactivate
14032              * Fires before a tab is de-activated - can be used to do validation on a form.
14033              * @param {Roo.bootstrap.TabPanel} this
14034              * @return {Boolean} false if there is an error
14035             
14036          */
14037         'beforedeactivate': true
14038      });
14039     
14040     this.tabId = this.tabId || Roo.id();
14041   
14042 };
14043
14044 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
14045     
14046     active: false,
14047     html: false,
14048     tabId: false,
14049     navId : false,
14050     
14051     getAutoCreate : function(){
14052         var cfg = {
14053             tag: 'div',
14054             // item is needed for carousel - not sure if it has any effect otherwise
14055             cls: 'tab-pane item',
14056             html: this.html || ''
14057         };
14058         
14059         if(this.active){
14060             cfg.cls += ' active';
14061         }
14062         
14063         if(this.tabId){
14064             cfg.tabId = this.tabId;
14065         }
14066         
14067         
14068         return cfg;
14069     },
14070     
14071     initEvents:  function()
14072     {
14073         Roo.log('-------- init events on tab panel ---------');
14074         
14075         var p = this.parent();
14076         this.navId = this.navId || p.navId;
14077         
14078         if (typeof(this.navId) != 'undefined') {
14079             // not really needed.. but just in case.. parent should be a NavGroup.
14080             var tg = Roo.bootstrap.TabGroup.get(this.navId);
14081             Roo.log(['register', tg, this]);
14082             tg.register(this);
14083         }
14084     },
14085     
14086     
14087     onRender : function(ct, position)
14088     {
14089        // Roo.log("Call onRender: " + this.xtype);
14090         
14091         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
14092         
14093         
14094         
14095         
14096         
14097     },
14098     
14099     setActive: function(state)
14100     {
14101         Roo.log("panel - set active " + this.tabId + "=" + state);
14102         
14103         this.active = state;
14104         if (!state) {
14105             this.el.removeClass('active');
14106             
14107         } else  if (!this.el.hasClass('active')) {
14108             this.el.addClass('active');
14109         }
14110         this.fireEvent('changed', this, state);
14111     }
14112     
14113     
14114 });
14115  
14116
14117  
14118
14119  /*
14120  * - LGPL
14121  *
14122  * DateField
14123  * 
14124  */
14125
14126 /**
14127  * @class Roo.bootstrap.DateField
14128  * @extends Roo.bootstrap.Input
14129  * Bootstrap DateField class
14130  * @cfg {Number} weekStart default 0
14131  * @cfg {Number} weekStart default 0
14132  * @cfg {Number} viewMode default empty, (months|years)
14133  * @cfg {Number} minViewMode default empty, (months|years)
14134  * @cfg {Number} startDate default -Infinity
14135  * @cfg {Number} endDate default Infinity
14136  * @cfg {Boolean} todayHighlight default false
14137  * @cfg {Boolean} todayBtn default false
14138  * @cfg {Boolean} calendarWeeks default false
14139  * @cfg {Object} daysOfWeekDisabled default empty
14140  * 
14141  * @cfg {Boolean} keyboardNavigation default true
14142  * @cfg {String} language default en
14143  * 
14144  * @constructor
14145  * Create a new DateField
14146  * @param {Object} config The config object
14147  */
14148
14149 Roo.bootstrap.DateField = function(config){
14150     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
14151      this.addEvents({
14152             /**
14153              * @event show
14154              * Fires when this field show.
14155              * @param {Roo.bootstrap.DateField} this
14156              * @param {Mixed} date The date value
14157              */
14158             show : true,
14159             /**
14160              * @event show
14161              * Fires when this field hide.
14162              * @param {Roo.bootstrap.DateField} this
14163              * @param {Mixed} date The date value
14164              */
14165             hide : true,
14166             /**
14167              * @event select
14168              * Fires when select a date.
14169              * @param {Roo.bootstrap.DateField} this
14170              * @param {Mixed} date The date value
14171              */
14172             select : true
14173         });
14174 };
14175
14176 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
14177     
14178     /**
14179      * @cfg {String} format
14180      * The default date format string which can be overriden for localization support.  The format must be
14181      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
14182      */
14183     format : "m/d/y",
14184     /**
14185      * @cfg {String} altFormats
14186      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
14187      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
14188      */
14189     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
14190     
14191     weekStart : 0,
14192     
14193     viewMode : '',
14194     
14195     minViewMode : '',
14196     
14197     todayHighlight : false,
14198     
14199     todayBtn: false,
14200     
14201     language: 'en',
14202     
14203     keyboardNavigation: true,
14204     
14205     calendarWeeks: false,
14206     
14207     startDate: -Infinity,
14208     
14209     endDate: Infinity,
14210     
14211     daysOfWeekDisabled: [],
14212     
14213     _events: [],
14214     
14215     UTCDate: function()
14216     {
14217         return new Date(Date.UTC.apply(Date, arguments));
14218     },
14219     
14220     UTCToday: function()
14221     {
14222         var today = new Date();
14223         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
14224     },
14225     
14226     getDate: function() {
14227             var d = this.getUTCDate();
14228             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
14229     },
14230     
14231     getUTCDate: function() {
14232             return this.date;
14233     },
14234     
14235     setDate: function(d) {
14236             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
14237     },
14238     
14239     setUTCDate: function(d) {
14240             this.date = d;
14241             this.setValue(this.formatDate(this.date));
14242     },
14243         
14244     onRender: function(ct, position)
14245     {
14246         
14247         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
14248         
14249         this.language = this.language || 'en';
14250         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
14251         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
14252         
14253         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
14254         this.format = this.format || 'm/d/y';
14255         this.isInline = false;
14256         this.isInput = true;
14257         this.component = this.el.select('.add-on', true).first() || false;
14258         this.component = (this.component && this.component.length === 0) ? false : this.component;
14259         this.hasInput = this.component && this.inputEL().length;
14260         
14261         if (typeof(this.minViewMode === 'string')) {
14262             switch (this.minViewMode) {
14263                 case 'months':
14264                     this.minViewMode = 1;
14265                     break;
14266                 case 'years':
14267                     this.minViewMode = 2;
14268                     break;
14269                 default:
14270                     this.minViewMode = 0;
14271                     break;
14272             }
14273         }
14274         
14275         if (typeof(this.viewMode === 'string')) {
14276             switch (this.viewMode) {
14277                 case 'months':
14278                     this.viewMode = 1;
14279                     break;
14280                 case 'years':
14281                     this.viewMode = 2;
14282                     break;
14283                 default:
14284                     this.viewMode = 0;
14285                     break;
14286             }
14287         }
14288                 
14289         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
14290         
14291         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14292         
14293         this.picker().on('mousedown', this.onMousedown, this);
14294         this.picker().on('click', this.onClick, this);
14295         
14296         this.picker().addClass('datepicker-dropdown');
14297         
14298         this.startViewMode = this.viewMode;
14299         
14300         
14301         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
14302             if(!this.calendarWeeks){
14303                 v.remove();
14304                 return;
14305             };
14306             
14307             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
14308             v.attr('colspan', function(i, val){
14309                 return parseInt(val) + 1;
14310             });
14311         })
14312                         
14313         
14314         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
14315         
14316         this.setStartDate(this.startDate);
14317         this.setEndDate(this.endDate);
14318         
14319         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
14320         
14321         this.fillDow();
14322         this.fillMonths();
14323         this.update();
14324         this.showMode();
14325         
14326         if(this.isInline) {
14327             this.show();
14328         }
14329     },
14330     
14331     picker : function()
14332     {
14333         return this.el.select('.datepicker', true).first();
14334     },
14335     
14336     fillDow: function()
14337     {
14338         var dowCnt = this.weekStart;
14339         
14340         var dow = {
14341             tag: 'tr',
14342             cn: [
14343                 
14344             ]
14345         };
14346         
14347         if(this.calendarWeeks){
14348             dow.cn.push({
14349                 tag: 'th',
14350                 cls: 'cw',
14351                 html: '&nbsp;'
14352             })
14353         }
14354         
14355         while (dowCnt < this.weekStart + 7) {
14356             dow.cn.push({
14357                 tag: 'th',
14358                 cls: 'dow',
14359                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
14360             });
14361         }
14362         
14363         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
14364     },
14365     
14366     fillMonths: function()
14367     {    
14368         var i = 0
14369         var months = this.picker().select('>.datepicker-months td', true).first();
14370         
14371         months.dom.innerHTML = '';
14372         
14373         while (i < 12) {
14374             var month = {
14375                 tag: 'span',
14376                 cls: 'month',
14377                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
14378             }
14379             
14380             months.createChild(month);
14381         }
14382         
14383     },
14384     
14385     update: function()
14386     {
14387         
14388         this.date = (typeof(this.date) === 'undefined') ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
14389         
14390         if (this.date < this.startDate) {
14391             this.viewDate = new Date(this.startDate);
14392         } else if (this.date > this.endDate) {
14393             this.viewDate = new Date(this.endDate);
14394         } else {
14395             this.viewDate = new Date(this.date);
14396         }
14397         
14398         this.fill();
14399     },
14400     
14401     fill: function() 
14402     {
14403         var d = new Date(this.viewDate),
14404                 year = d.getUTCFullYear(),
14405                 month = d.getUTCMonth(),
14406                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
14407                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
14408                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
14409                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
14410                 currentDate = this.date && this.date.valueOf(),
14411                 today = this.UTCToday();
14412         
14413         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
14414         
14415 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
14416         
14417 //        this.picker.select('>tfoot th.today').
14418 //                                              .text(dates[this.language].today)
14419 //                                              .toggle(this.todayBtn !== false);
14420     
14421         this.updateNavArrows();
14422         this.fillMonths();
14423                                                 
14424         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
14425         
14426         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
14427          
14428         prevMonth.setUTCDate(day);
14429         
14430         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
14431         
14432         var nextMonth = new Date(prevMonth);
14433         
14434         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
14435         
14436         nextMonth = nextMonth.valueOf();
14437         
14438         var fillMonths = false;
14439         
14440         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
14441         
14442         while(prevMonth.valueOf() < nextMonth) {
14443             var clsName = '';
14444             
14445             if (prevMonth.getUTCDay() === this.weekStart) {
14446                 if(fillMonths){
14447                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
14448                 }
14449                     
14450                 fillMonths = {
14451                     tag: 'tr',
14452                     cn: []
14453                 };
14454                 
14455                 if(this.calendarWeeks){
14456                     // ISO 8601: First week contains first thursday.
14457                     // ISO also states week starts on Monday, but we can be more abstract here.
14458                     var
14459                     // Start of current week: based on weekstart/current date
14460                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
14461                     // Thursday of this week
14462                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
14463                     // First Thursday of year, year from thursday
14464                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
14465                     // Calendar week: ms between thursdays, div ms per day, div 7 days
14466                     calWeek =  (th - yth) / 864e5 / 7 + 1;
14467                     
14468                     fillMonths.cn.push({
14469                         tag: 'td',
14470                         cls: 'cw',
14471                         html: calWeek
14472                     });
14473                 }
14474             }
14475             
14476             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
14477                 clsName += ' old';
14478             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
14479                 clsName += ' new';
14480             }
14481             if (this.todayHighlight &&
14482                 prevMonth.getUTCFullYear() == today.getFullYear() &&
14483                 prevMonth.getUTCMonth() == today.getMonth() &&
14484                 prevMonth.getUTCDate() == today.getDate()) {
14485                 clsName += ' today';
14486             }
14487             
14488             if (currentDate && prevMonth.valueOf() === currentDate) {
14489                 clsName += ' active';
14490             }
14491             
14492             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
14493                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
14494                     clsName += ' disabled';
14495             }
14496             
14497             fillMonths.cn.push({
14498                 tag: 'td',
14499                 cls: 'day ' + clsName,
14500                 html: prevMonth.getDate()
14501             })
14502             
14503             prevMonth.setDate(prevMonth.getDate()+1);
14504         }
14505           
14506         var currentYear = this.date && this.date.getUTCFullYear();
14507         var currentMonth = this.date && this.date.getUTCMonth();
14508         
14509         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
14510         
14511         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
14512             v.removeClass('active');
14513             
14514             if(currentYear === year && k === currentMonth){
14515                 v.addClass('active');
14516             }
14517             
14518             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
14519                 v.addClass('disabled');
14520             }
14521             
14522         });
14523         
14524         
14525         year = parseInt(year/10, 10) * 10;
14526         
14527         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
14528         
14529         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
14530         
14531         year -= 1;
14532         for (var i = -1; i < 11; i++) {
14533             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
14534                 tag: 'span',
14535                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
14536                 html: year
14537             })
14538             
14539             year += 1;
14540         }
14541     },
14542     
14543     showMode: function(dir) 
14544     {
14545         if (dir) {
14546             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
14547         }
14548         Roo.each(this.picker().select('>div',true).elements, function(v){
14549             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14550             v.hide();
14551         });
14552         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
14553     },
14554     
14555     place: function()
14556     {
14557         if(this.isInline) return;
14558         
14559         this.picker().removeClass(['bottom', 'top']);
14560         
14561         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
14562             /*
14563              * place to the top of element!
14564              *
14565              */
14566             
14567             this.picker().addClass('top');
14568             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
14569             
14570             return;
14571         }
14572         
14573         this.picker().addClass('bottom');
14574         
14575         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
14576     },
14577     
14578     parseDate : function(value)
14579     {
14580         if(!value || value instanceof Date){
14581             return value;
14582         }
14583         var v = Date.parseDate(value, this.format);
14584         if (!v && this.useIso) {
14585             v = Date.parseDate(value, 'Y-m-d');
14586         }
14587         if(!v && this.altFormats){
14588             if(!this.altFormatsArray){
14589                 this.altFormatsArray = this.altFormats.split("|");
14590             }
14591             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
14592                 v = Date.parseDate(value, this.altFormatsArray[i]);
14593             }
14594         }
14595         return v;
14596     },
14597     
14598     formatDate : function(date, fmt)
14599     {
14600         return (!date || !(date instanceof Date)) ?
14601         date : date.dateFormat(fmt || this.format);
14602     },
14603     
14604     onFocus : function()
14605     {
14606         Roo.bootstrap.DateField.superclass.onFocus.call(this);
14607         this.show();
14608     },
14609     
14610     onBlur : function()
14611     {
14612         Roo.bootstrap.DateField.superclass.onBlur.call(this);
14613         
14614         var d = this.inputEl().getValue();
14615         
14616         if(d && d.length){
14617             this.setValue(d);
14618         }
14619                 
14620         this.hide();
14621     },
14622     
14623     show : function()
14624     {
14625         this.picker().show();
14626         this.update();
14627         this.place();
14628         
14629         this.fireEvent('show', this, this.date);
14630     },
14631     
14632     hide : function()
14633     {
14634         if(this.isInline) return;
14635         this.picker().hide();
14636         this.viewMode = this.startViewMode;
14637         this.showMode();
14638         
14639         this.fireEvent('hide', this, this.date);
14640         
14641     },
14642     
14643     onMousedown: function(e)
14644     {
14645         e.stopPropagation();
14646         e.preventDefault();
14647     },
14648     
14649     keyup: function(e)
14650     {
14651         Roo.bootstrap.DateField.superclass.keyup.call(this);
14652         this.update();
14653     },
14654
14655     setValue: function(v)
14656     {
14657         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
14658         
14659         var d = new Date(v);
14660         
14661         if(isNaN(d.getTime())){
14662             return;
14663         }
14664         
14665         this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
14666
14667         this.update();
14668
14669         this.fireEvent('select', this, this.date);
14670         
14671     },
14672     
14673     getValue: function()
14674     {
14675         return this.formatDate(this.date);
14676     },
14677     
14678     fireKey: function(e)
14679     {
14680         if (!this.picker().isVisible()){
14681             if (e.keyCode == 27) // allow escape to hide and re-show picker
14682                 this.show();
14683             return;
14684         }
14685         
14686         var dateChanged = false,
14687         dir, day, month,
14688         newDate, newViewDate;
14689         
14690         switch(e.keyCode){
14691             case 27: // escape
14692                 this.hide();
14693                 e.preventDefault();
14694                 break;
14695             case 37: // left
14696             case 39: // right
14697                 if (!this.keyboardNavigation) break;
14698                 dir = e.keyCode == 37 ? -1 : 1;
14699                 
14700                 if (e.ctrlKey){
14701                     newDate = this.moveYear(this.date, dir);
14702                     newViewDate = this.moveYear(this.viewDate, dir);
14703                 } else if (e.shiftKey){
14704                     newDate = this.moveMonth(this.date, dir);
14705                     newViewDate = this.moveMonth(this.viewDate, dir);
14706                 } else {
14707                     newDate = new Date(this.date);
14708                     newDate.setUTCDate(this.date.getUTCDate() + dir);
14709                     newViewDate = new Date(this.viewDate);
14710                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
14711                 }
14712                 if (this.dateWithinRange(newDate)){
14713                     this.date = newDate;
14714                     this.viewDate = newViewDate;
14715                     this.setValue(this.formatDate(this.date));
14716 //                    this.update();
14717                     e.preventDefault();
14718                     dateChanged = true;
14719                 }
14720                 break;
14721             case 38: // up
14722             case 40: // down
14723                 if (!this.keyboardNavigation) break;
14724                 dir = e.keyCode == 38 ? -1 : 1;
14725                 if (e.ctrlKey){
14726                     newDate = this.moveYear(this.date, dir);
14727                     newViewDate = this.moveYear(this.viewDate, dir);
14728                 } else if (e.shiftKey){
14729                     newDate = this.moveMonth(this.date, dir);
14730                     newViewDate = this.moveMonth(this.viewDate, dir);
14731                 } else {
14732                     newDate = new Date(this.date);
14733                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
14734                     newViewDate = new Date(this.viewDate);
14735                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
14736                 }
14737                 if (this.dateWithinRange(newDate)){
14738                     this.date = newDate;
14739                     this.viewDate = newViewDate;
14740                     this.setValue(this.formatDate(this.date));
14741 //                    this.update();
14742                     e.preventDefault();
14743                     dateChanged = true;
14744                 }
14745                 break;
14746             case 13: // enter
14747                 this.setValue(this.formatDate(this.date));
14748                 this.hide();
14749                 e.preventDefault();
14750                 break;
14751             case 9: // tab
14752                 this.setValue(this.formatDate(this.date));
14753                 this.hide();
14754                 break;
14755             case 16: // shift
14756             case 17: // ctrl
14757             case 18: // alt
14758                 break;
14759             default :
14760                 this.hide();
14761                 
14762         }
14763     },
14764     
14765     
14766     onClick: function(e) 
14767     {
14768         e.stopPropagation();
14769         e.preventDefault();
14770         
14771         var target = e.getTarget();
14772         
14773         if(target.nodeName.toLowerCase() === 'i'){
14774             target = Roo.get(target).dom.parentNode;
14775         }
14776         
14777         var nodeName = target.nodeName;
14778         var className = target.className;
14779         var html = target.innerHTML;
14780         
14781         switch(nodeName.toLowerCase()) {
14782             case 'th':
14783                 switch(className) {
14784                     case 'switch':
14785                         this.showMode(1);
14786                         break;
14787                     case 'prev':
14788                     case 'next':
14789                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
14790                         switch(this.viewMode){
14791                                 case 0:
14792                                         this.viewDate = this.moveMonth(this.viewDate, dir);
14793                                         break;
14794                                 case 1:
14795                                 case 2:
14796                                         this.viewDate = this.moveYear(this.viewDate, dir);
14797                                         break;
14798                         }
14799                         this.fill();
14800                         break;
14801                     case 'today':
14802                         var date = new Date();
14803                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
14804 //                        this.fill()
14805                         this.setValue(this.formatDate(this.date));
14806                         
14807                         this.hide();
14808                         break;
14809                 }
14810                 break;
14811             case 'span':
14812                 if (className.indexOf('disabled') === -1) {
14813                     this.viewDate.setUTCDate(1);
14814                     if (className.indexOf('month') !== -1) {
14815                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
14816                     } else {
14817                         var year = parseInt(html, 10) || 0;
14818                         this.viewDate.setUTCFullYear(year);
14819                         
14820                     }
14821                     this.showMode(-1);
14822                     this.fill();
14823                 }
14824                 break;
14825                 
14826             case 'td':
14827                 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
14828                     var day = parseInt(html, 10) || 1;
14829                     var year = this.viewDate.getUTCFullYear(),
14830                         month = this.viewDate.getUTCMonth();
14831
14832                     if (className.indexOf('old') !== -1) {
14833                         if(month === 0 ){
14834                             month = 11;
14835                             year -= 1;
14836                         }else{
14837                             month -= 1;
14838                         }
14839                     } else if (className.indexOf('new') !== -1) {
14840                         if (month == 11) {
14841                             month = 0;
14842                             year += 1;
14843                         } else {
14844                             month += 1;
14845                         }
14846                     }
14847                     this.date = this.UTCDate(year, month, day,0,0,0,0);
14848                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
14849 //                    this.fill();
14850                     this.setValue(this.formatDate(this.date));
14851                     this.hide();
14852                 }
14853                 break;
14854         }
14855     },
14856     
14857     setStartDate: function(startDate)
14858     {
14859         this.startDate = startDate || -Infinity;
14860         if (this.startDate !== -Infinity) {
14861             this.startDate = this.parseDate(this.startDate);
14862         }
14863         this.update();
14864         this.updateNavArrows();
14865     },
14866
14867     setEndDate: function(endDate)
14868     {
14869         this.endDate = endDate || Infinity;
14870         if (this.endDate !== Infinity) {
14871             this.endDate = this.parseDate(this.endDate);
14872         }
14873         this.update();
14874         this.updateNavArrows();
14875     },
14876     
14877     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
14878     {
14879         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
14880         if (typeof(this.daysOfWeekDisabled) !== 'object') {
14881             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
14882         }
14883         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
14884             return parseInt(d, 10);
14885         });
14886         this.update();
14887         this.updateNavArrows();
14888     },
14889     
14890     updateNavArrows: function() 
14891     {
14892         var d = new Date(this.viewDate),
14893         year = d.getUTCFullYear(),
14894         month = d.getUTCMonth();
14895         
14896         Roo.each(this.picker().select('.prev', true).elements, function(v){
14897             v.show();
14898             switch (this.viewMode) {
14899                 case 0:
14900
14901                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
14902                         v.hide();
14903                     }
14904                     break;
14905                 case 1:
14906                 case 2:
14907                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
14908                         v.hide();
14909                     }
14910                     break;
14911             }
14912         });
14913         
14914         Roo.each(this.picker().select('.next', true).elements, function(v){
14915             v.show();
14916             switch (this.viewMode) {
14917                 case 0:
14918
14919                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
14920                         v.hide();
14921                     }
14922                     break;
14923                 case 1:
14924                 case 2:
14925                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
14926                         v.hide();
14927                     }
14928                     break;
14929             }
14930         })
14931     },
14932     
14933     moveMonth: function(date, dir)
14934     {
14935         if (!dir) return date;
14936         var new_date = new Date(date.valueOf()),
14937         day = new_date.getUTCDate(),
14938         month = new_date.getUTCMonth(),
14939         mag = Math.abs(dir),
14940         new_month, test;
14941         dir = dir > 0 ? 1 : -1;
14942         if (mag == 1){
14943             test = dir == -1
14944             // If going back one month, make sure month is not current month
14945             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
14946             ? function(){
14947                 return new_date.getUTCMonth() == month;
14948             }
14949             // If going forward one month, make sure month is as expected
14950             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
14951             : function(){
14952                 return new_date.getUTCMonth() != new_month;
14953             };
14954             new_month = month + dir;
14955             new_date.setUTCMonth(new_month);
14956             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
14957             if (new_month < 0 || new_month > 11)
14958                 new_month = (new_month + 12) % 12;
14959         } else {
14960             // For magnitudes >1, move one month at a time...
14961             for (var i=0; i<mag; i++)
14962                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
14963                 new_date = this.moveMonth(new_date, dir);
14964             // ...then reset the day, keeping it in the new month
14965             new_month = new_date.getUTCMonth();
14966             new_date.setUTCDate(day);
14967             test = function(){
14968                 return new_month != new_date.getUTCMonth();
14969             };
14970         }
14971         // Common date-resetting loop -- if date is beyond end of month, make it
14972         // end of month
14973         while (test()){
14974             new_date.setUTCDate(--day);
14975             new_date.setUTCMonth(new_month);
14976         }
14977         return new_date;
14978     },
14979
14980     moveYear: function(date, dir)
14981     {
14982         return this.moveMonth(date, dir*12);
14983     },
14984
14985     dateWithinRange: function(date)
14986     {
14987         return date >= this.startDate && date <= this.endDate;
14988     },
14989
14990     
14991     remove: function() 
14992     {
14993         this.picker().remove();
14994     }
14995    
14996 });
14997
14998 Roo.apply(Roo.bootstrap.DateField,  {
14999     
15000     head : {
15001         tag: 'thead',
15002         cn: [
15003         {
15004             tag: 'tr',
15005             cn: [
15006             {
15007                 tag: 'th',
15008                 cls: 'prev',
15009                 html: '<i class="fa fa-arrow-left"/>'
15010             },
15011             {
15012                 tag: 'th',
15013                 cls: 'switch',
15014                 colspan: '5'
15015             },
15016             {
15017                 tag: 'th',
15018                 cls: 'next',
15019                 html: '<i class="fa fa-arrow-right"/>'
15020             }
15021
15022             ]
15023         }
15024         ]
15025     },
15026     
15027     content : {
15028         tag: 'tbody',
15029         cn: [
15030         {
15031             tag: 'tr',
15032             cn: [
15033             {
15034                 tag: 'td',
15035                 colspan: '7'
15036             }
15037             ]
15038         }
15039         ]
15040     },
15041     
15042     footer : {
15043         tag: 'tfoot',
15044         cn: [
15045         {
15046             tag: 'tr',
15047             cn: [
15048             {
15049                 tag: 'th',
15050                 colspan: '7',
15051                 cls: 'today'
15052             }
15053                     
15054             ]
15055         }
15056         ]
15057     },
15058     
15059     dates:{
15060         en: {
15061             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
15062             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
15063             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
15064             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
15065             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
15066             today: "Today"
15067         }
15068     },
15069     
15070     modes: [
15071     {
15072         clsName: 'days',
15073         navFnc: 'Month',
15074         navStep: 1
15075     },
15076     {
15077         clsName: 'months',
15078         navFnc: 'FullYear',
15079         navStep: 1
15080     },
15081     {
15082         clsName: 'years',
15083         navFnc: 'FullYear',
15084         navStep: 10
15085     }]
15086 });
15087
15088 Roo.apply(Roo.bootstrap.DateField,  {
15089   
15090     template : {
15091         tag: 'div',
15092         cls: 'datepicker dropdown-menu',
15093         cn: [
15094         {
15095             tag: 'div',
15096             cls: 'datepicker-days',
15097             cn: [
15098             {
15099                 tag: 'table',
15100                 cls: 'table-condensed',
15101                 cn:[
15102                 Roo.bootstrap.DateField.head,
15103                 {
15104                     tag: 'tbody'
15105                 },
15106                 Roo.bootstrap.DateField.footer
15107                 ]
15108             }
15109             ]
15110         },
15111         {
15112             tag: 'div',
15113             cls: 'datepicker-months',
15114             cn: [
15115             {
15116                 tag: 'table',
15117                 cls: 'table-condensed',
15118                 cn:[
15119                 Roo.bootstrap.DateField.head,
15120                 Roo.bootstrap.DateField.content,
15121                 Roo.bootstrap.DateField.footer
15122                 ]
15123             }
15124             ]
15125         },
15126         {
15127             tag: 'div',
15128             cls: 'datepicker-years',
15129             cn: [
15130             {
15131                 tag: 'table',
15132                 cls: 'table-condensed',
15133                 cn:[
15134                 Roo.bootstrap.DateField.head,
15135                 Roo.bootstrap.DateField.content,
15136                 Roo.bootstrap.DateField.footer
15137                 ]
15138             }
15139             ]
15140         }
15141         ]
15142     }
15143 });
15144
15145  
15146
15147  /*
15148  * - LGPL
15149  *
15150  * TimeField
15151  * 
15152  */
15153
15154 /**
15155  * @class Roo.bootstrap.TimeField
15156  * @extends Roo.bootstrap.Input
15157  * Bootstrap DateField class
15158  * 
15159  * 
15160  * @constructor
15161  * Create a new TimeField
15162  * @param {Object} config The config object
15163  */
15164
15165 Roo.bootstrap.TimeField = function(config){
15166     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
15167     this.addEvents({
15168             /**
15169              * @event show
15170              * Fires when this field show.
15171              * @param {Roo.bootstrap.DateField} this
15172              * @param {Mixed} date The date value
15173              */
15174             show : true,
15175             /**
15176              * @event show
15177              * Fires when this field hide.
15178              * @param {Roo.bootstrap.DateField} this
15179              * @param {Mixed} date The date value
15180              */
15181             hide : true,
15182             /**
15183              * @event select
15184              * Fires when select a date.
15185              * @param {Roo.bootstrap.DateField} this
15186              * @param {Mixed} date The date value
15187              */
15188             select : true
15189         });
15190 };
15191
15192 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
15193     
15194     /**
15195      * @cfg {String} format
15196      * The default time format string which can be overriden for localization support.  The format must be
15197      * valid according to {@link Date#parseDate} (defaults to 'H:i').
15198      */
15199     format : "H:i",
15200        
15201     onRender: function(ct, position)
15202     {
15203         
15204         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
15205                 
15206         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
15207         
15208         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15209         
15210         this.pop = this.picker().select('>.datepicker-time',true).first();
15211         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block' 
15212         
15213         this.picker().on('mousedown', this.onMousedown, this);
15214         this.picker().on('click', this.onClick, this);
15215         
15216         this.picker().addClass('datepicker-dropdown');
15217     
15218         this.fillTime();
15219         this.update();
15220             
15221         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
15222         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
15223         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
15224         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
15225         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
15226         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
15227
15228     },
15229     
15230     fireKey: function(e){
15231         if (!this.picker().isVisible()){
15232             if (e.keyCode == 27) // allow escape to hide and re-show picker
15233                 this.show();
15234             return;
15235         }
15236
15237         e.preventDefault();
15238         
15239         switch(e.keyCode){
15240             case 27: // escape
15241                 this.hide();
15242                 break;
15243             case 37: // left
15244             case 39: // right
15245                 this.onTogglePeriod();
15246                 break;
15247             case 38: // up
15248                 this.onIncrementMinutes();
15249                 break;
15250             case 40: // down
15251                 this.onDecrementMinutes();
15252                 break;
15253             case 13: // enter
15254             case 9: // tab
15255                 this.setTime();
15256                 break;
15257         }
15258     },
15259     
15260     onClick: function(e) {
15261         e.stopPropagation();
15262         e.preventDefault();
15263     },
15264     
15265     picker : function()
15266     {
15267         return this.el.select('.datepicker', true).first();
15268     },
15269     
15270     fillTime: function()
15271     {    
15272         var time = this.pop.select('tbody', true).first();
15273         
15274         time.dom.innerHTML = '';
15275         
15276         time.createChild({
15277             tag: 'tr',
15278             cn: [
15279                 {
15280                     tag: 'td',
15281                     cn: [
15282                         {
15283                             tag: 'a',
15284                             href: '#',
15285                             cls: 'btn',
15286                             cn: [
15287                                 {
15288                                     tag: 'span',
15289                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
15290                                 }
15291                             ]
15292                         } 
15293                     ]
15294                 },
15295                 {
15296                     tag: 'td',
15297                     cls: 'separator'
15298                 },
15299                 {
15300                     tag: 'td',
15301                     cn: [
15302                         {
15303                             tag: 'a',
15304                             href: '#',
15305                             cls: 'btn',
15306                             cn: [
15307                                 {
15308                                     tag: 'span',
15309                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
15310                                 }
15311                             ]
15312                         }
15313                     ]
15314                 },
15315                 {
15316                     tag: 'td',
15317                     cls: 'separator'
15318                 }
15319             ]
15320         });
15321         
15322         time.createChild({
15323             tag: 'tr',
15324             cn: [
15325                 {
15326                     tag: 'td',
15327                     cn: [
15328                         {
15329                             tag: 'span',
15330                             cls: 'timepicker-hour',
15331                             html: '00'
15332                         }  
15333                     ]
15334                 },
15335                 {
15336                     tag: 'td',
15337                     cls: 'separator',
15338                     html: ':'
15339                 },
15340                 {
15341                     tag: 'td',
15342                     cn: [
15343                         {
15344                             tag: 'span',
15345                             cls: 'timepicker-minute',
15346                             html: '00'
15347                         }  
15348                     ]
15349                 },
15350                 {
15351                     tag: 'td',
15352                     cls: 'separator'
15353                 },
15354                 {
15355                     tag: 'td',
15356                     cn: [
15357                         {
15358                             tag: 'button',
15359                             type: 'button',
15360                             cls: 'btn btn-primary period',
15361                             html: 'AM'
15362                             
15363                         }
15364                     ]
15365                 }
15366             ]
15367         });
15368         
15369         time.createChild({
15370             tag: 'tr',
15371             cn: [
15372                 {
15373                     tag: 'td',
15374                     cn: [
15375                         {
15376                             tag: 'a',
15377                             href: '#',
15378                             cls: 'btn',
15379                             cn: [
15380                                 {
15381                                     tag: 'span',
15382                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
15383                                 }
15384                             ]
15385                         }
15386                     ]
15387                 },
15388                 {
15389                     tag: 'td',
15390                     cls: 'separator'
15391                 },
15392                 {
15393                     tag: 'td',
15394                     cn: [
15395                         {
15396                             tag: 'a',
15397                             href: '#',
15398                             cls: 'btn',
15399                             cn: [
15400                                 {
15401                                     tag: 'span',
15402                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
15403                                 }
15404                             ]
15405                         }
15406                     ]
15407                 },
15408                 {
15409                     tag: 'td',
15410                     cls: 'separator'
15411                 }
15412             ]
15413         });
15414         
15415     },
15416     
15417     update: function()
15418     {
15419         
15420         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
15421         
15422         this.fill();
15423     },
15424     
15425     fill: function() 
15426     {
15427         var hours = this.time.getHours();
15428         var minutes = this.time.getMinutes();
15429         var period = 'AM';
15430         
15431         if(hours > 11){
15432             period = 'PM';
15433         }
15434         
15435         if(hours == 0){
15436             hours = 12;
15437         }
15438         
15439         
15440         if(hours > 12){
15441             hours = hours - 12;
15442         }
15443         
15444         if(hours < 10){
15445             hours = '0' + hours;
15446         }
15447         
15448         if(minutes < 10){
15449             minutes = '0' + minutes;
15450         }
15451         
15452         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
15453         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
15454         this.pop.select('button', true).first().dom.innerHTML = period;
15455         
15456     },
15457     
15458     place: function()
15459     {   
15460         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
15461         
15462         var cls = ['bottom'];
15463         
15464         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
15465             cls.pop();
15466             cls.push('top');
15467         }
15468         
15469         cls.push('right');
15470         
15471         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
15472             cls.pop();
15473             cls.push('left');
15474         }
15475         
15476         this.picker().addClass(cls.join('-'));
15477         
15478         var _this = this;
15479         
15480         Roo.each(cls, function(c){
15481             if(c == 'bottom'){
15482                 _this.picker().setTop(_this.inputEl().getHeight());
15483                 return;
15484             }
15485             if(c == 'top'){
15486                 _this.picker().setTop(0 - _this.picker().getHeight());
15487                 return;
15488             }
15489             
15490             if(c == 'left'){
15491                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
15492                 return;
15493             }
15494             if(c == 'right'){
15495                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
15496                 return;
15497             }
15498         });
15499         
15500     },
15501   
15502     onFocus : function()
15503     {
15504         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
15505         this.show();
15506     },
15507     
15508     onBlur : function()
15509     {
15510         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
15511         this.hide();
15512     },
15513     
15514     show : function()
15515     {
15516         this.picker().show();
15517         this.pop.show();
15518         this.update();
15519         this.place();
15520         
15521         this.fireEvent('show', this, this.date);
15522     },
15523     
15524     hide : function()
15525     {
15526         this.picker().hide();
15527         this.pop.hide();
15528         
15529         this.fireEvent('hide', this, this.date);
15530     },
15531     
15532     setTime : function()
15533     {
15534         this.hide();
15535         this.setValue(this.time.format(this.format));
15536         
15537         this.fireEvent('select', this, this.date);
15538         
15539         
15540     },
15541     
15542     onMousedown: function(e){
15543         e.stopPropagation();
15544         e.preventDefault();
15545     },
15546     
15547     onIncrementHours: function()
15548     {
15549         Roo.log('onIncrementHours');
15550         this.time = this.time.add(Date.HOUR, 1);
15551         this.update();
15552         
15553     },
15554     
15555     onDecrementHours: function()
15556     {
15557         Roo.log('onDecrementHours');
15558         this.time = this.time.add(Date.HOUR, -1);
15559         this.update();
15560     },
15561     
15562     onIncrementMinutes: function()
15563     {
15564         Roo.log('onIncrementMinutes');
15565         this.time = this.time.add(Date.MINUTE, 1);
15566         this.update();
15567     },
15568     
15569     onDecrementMinutes: function()
15570     {
15571         Roo.log('onDecrementMinutes');
15572         this.time = this.time.add(Date.MINUTE, -1);
15573         this.update();
15574     },
15575     
15576     onTogglePeriod: function()
15577     {
15578         Roo.log('onTogglePeriod');
15579         this.time = this.time.add(Date.HOUR, 12);
15580         this.update();
15581     }
15582     
15583    
15584 });
15585
15586 Roo.apply(Roo.bootstrap.TimeField,  {
15587     
15588     content : {
15589         tag: 'tbody',
15590         cn: [
15591             {
15592                 tag: 'tr',
15593                 cn: [
15594                 {
15595                     tag: 'td',
15596                     colspan: '7'
15597                 }
15598                 ]
15599             }
15600         ]
15601     },
15602     
15603     footer : {
15604         tag: 'tfoot',
15605         cn: [
15606             {
15607                 tag: 'tr',
15608                 cn: [
15609                 {
15610                     tag: 'th',
15611                     colspan: '7',
15612                     cls: '',
15613                     cn: [
15614                         {
15615                             tag: 'button',
15616                             cls: 'btn btn-info ok',
15617                             html: 'OK'
15618                         }
15619                     ]
15620                 }
15621
15622                 ]
15623             }
15624         ]
15625     }
15626 });
15627
15628 Roo.apply(Roo.bootstrap.TimeField,  {
15629   
15630     template : {
15631         tag: 'div',
15632         cls: 'datepicker dropdown-menu',
15633         cn: [
15634             {
15635                 tag: 'div',
15636                 cls: 'datepicker-time',
15637                 cn: [
15638                 {
15639                     tag: 'table',
15640                     cls: 'table-condensed',
15641                     cn:[
15642                     Roo.bootstrap.TimeField.content,
15643                     Roo.bootstrap.TimeField.footer
15644                     ]
15645                 }
15646                 ]
15647             }
15648         ]
15649     }
15650 });
15651
15652  
15653
15654  /*
15655  * - LGPL
15656  *
15657  * CheckBox
15658  * 
15659  */
15660
15661 /**
15662  * @class Roo.bootstrap.CheckBox
15663  * @extends Roo.bootstrap.Input
15664  * Bootstrap CheckBox class
15665  * 
15666  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
15667  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
15668  * @cfg {String} boxLabel The text that appears beside the checkbox
15669  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
15670  * @cfg {Boolean} checked initnal the element
15671  * 
15672  * 
15673  * @constructor
15674  * Create a new CheckBox
15675  * @param {Object} config The config object
15676  */
15677
15678 Roo.bootstrap.CheckBox = function(config){
15679     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
15680    
15681         this.addEvents({
15682             /**
15683             * @event check
15684             * Fires when the element is checked or unchecked.
15685             * @param {Roo.bootstrap.CheckBox} this This input
15686             * @param {Boolean} checked The new checked value
15687             */
15688            check : true
15689         });
15690 };
15691
15692 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
15693     
15694     inputType: 'checkbox',
15695     inputValue: 1,
15696     valueOff: 0,
15697     boxLabel: false,
15698     checked: false,
15699     weight : false,
15700     
15701     getAutoCreate : function()
15702     {
15703         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
15704         
15705         var id = Roo.id();
15706         
15707         var cfg = {};
15708         
15709         cfg.cls = 'form-group checkbox' //input-group
15710         
15711         
15712         
15713         
15714         var input =  {
15715             tag: 'input',
15716             id : id,
15717             type : this.inputType,
15718             value : (!this.checked) ? this.valueOff : this.inputValue,
15719             cls : 'roo-checkbox', //'form-box',
15720             placeholder : this.placeholder || ''
15721             
15722         };
15723         
15724         if (this.weight) { // Validity check?
15725             cfg.cls += " checkbox-" + this.weight;
15726         }
15727         
15728         if (this.disabled) {
15729             input.disabled=true;
15730         }
15731         
15732         if(this.checked){
15733             input.checked = this.checked;
15734         }
15735         
15736         if (this.name) {
15737             input.name = this.name;
15738         }
15739         
15740         if (this.size) {
15741             input.cls += ' input-' + this.size;
15742         }
15743         
15744         var settings=this;
15745         ['xs','sm','md','lg'].map(function(size){
15746             if (settings[size]) {
15747                 cfg.cls += ' col-' + size + '-' + settings[size];
15748             }
15749         });
15750         
15751        
15752         
15753         var inputblock = input;
15754         
15755         
15756         
15757         
15758         if (this.before || this.after) {
15759             
15760             inputblock = {
15761                 cls : 'input-group',
15762                 cn :  [] 
15763             };
15764             if (this.before) {
15765                 inputblock.cn.push({
15766                     tag :'span',
15767                     cls : 'input-group-addon',
15768                     html : this.before
15769                 });
15770             }
15771             inputblock.cn.push(input);
15772             if (this.after) {
15773                 inputblock.cn.push({
15774                     tag :'span',
15775                     cls : 'input-group-addon',
15776                     html : this.after
15777                 });
15778             }
15779             
15780         };
15781         
15782         if (align ==='left' && this.fieldLabel.length) {
15783                 Roo.log("left and has label");
15784                 cfg.cn = [
15785                     
15786                     {
15787                         tag: 'label',
15788                         'for' :  id,
15789                         cls : 'control-label col-md-' + this.labelWidth,
15790                         html : this.fieldLabel
15791                         
15792                     },
15793                     {
15794                         cls : "col-md-" + (12 - this.labelWidth), 
15795                         cn: [
15796                             inputblock
15797                         ]
15798                     }
15799                     
15800                 ];
15801         } else if ( this.fieldLabel.length) {
15802                 Roo.log(" label");
15803                 cfg.cn = [
15804                    
15805                     {
15806                         tag: this.boxLabel ? 'span' : 'label',
15807                         'for': id,
15808                         cls: 'control-label box-input-label',
15809                         //cls : 'input-group-addon',
15810                         html : this.fieldLabel
15811                         
15812                     },
15813                     
15814                     inputblock
15815                     
15816                 ];
15817
15818         } else {
15819             
15820                 Roo.log(" no label && no align");
15821                 cfg.cn = [  inputblock ] ;
15822                 
15823                 
15824         };
15825          if(this.boxLabel){
15826             cfg.cn.push( {
15827                 tag: 'label',
15828                 'for': id,
15829                 cls: 'box-label',
15830                 html: this.boxLabel
15831                 
15832             });
15833         }
15834         
15835         
15836        
15837         return cfg;
15838         
15839     },
15840     
15841     /**
15842      * return the real input element.
15843      */
15844     inputEl: function ()
15845     {
15846         return this.el.select('input.roo-checkbox',true).first();
15847     },
15848     
15849     label: function()
15850     {
15851         return this.el.select('label.control-label',true).first();
15852     },
15853     
15854     initEvents : function()
15855     {
15856 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
15857         
15858         this.inputEl().on('click', this.onClick,  this);
15859         
15860     },
15861     
15862     onClick : function()
15863     {   
15864         this.setChecked(!this.checked);
15865     },
15866     
15867     setChecked : function(state,suppressEvent)
15868     {
15869         this.checked = state;
15870         
15871         this.inputEl().dom.checked = state;
15872         
15873         if(suppressEvent !== true){
15874             this.fireEvent('check', this, state);
15875         }
15876         
15877         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
15878         
15879     },
15880     
15881     setValue : function(v,suppressEvent)
15882     {
15883         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
15884     }
15885     
15886 });
15887
15888  
15889 /*
15890  * - LGPL
15891  *
15892  * Radio
15893  * 
15894  */
15895
15896 /**
15897  * @class Roo.bootstrap.Radio
15898  * @extends Roo.bootstrap.CheckBox
15899  * Bootstrap Radio class
15900
15901  * @constructor
15902  * Create a new Radio
15903  * @param {Object} config The config object
15904  */
15905
15906 Roo.bootstrap.Radio = function(config){
15907     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
15908    
15909 };
15910
15911 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
15912     
15913     inputType: 'radio',
15914     inputValue: '',
15915     valueOff: '',
15916     
15917     getAutoCreate : function()
15918     {
15919         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
15920         
15921         var id = Roo.id();
15922         
15923         var cfg = {};
15924         
15925         cfg.cls = 'form-group radio' //input-group
15926         
15927         var input =  {
15928             tag: 'input',
15929             id : id,
15930             type : this.inputType,
15931             value : (!this.checked) ? this.valueOff : this.inputValue,
15932             cls : 'roo-radio',
15933             placeholder : this.placeholder || ''
15934             
15935         };
15936           if (this.weight) { // Validity check?
15937             cfg.cls += " radio-" + this.weight;
15938         }
15939         if (this.disabled) {
15940             input.disabled=true;
15941         }
15942         
15943         if(this.checked){
15944             input.checked = this.checked;
15945         }
15946         
15947         if (this.name) {
15948             input.name = this.name;
15949         }
15950         
15951         if (this.size) {
15952             input.cls += ' input-' + this.size;
15953         }
15954         
15955         var settings=this;
15956         ['xs','sm','md','lg'].map(function(size){
15957             if (settings[size]) {
15958                 cfg.cls += ' col-' + size + '-' + settings[size];
15959             }
15960         });
15961         
15962         var inputblock = input;
15963         
15964         if (this.before || this.after) {
15965             
15966             inputblock = {
15967                 cls : 'input-group',
15968                 cn :  [] 
15969             };
15970             if (this.before) {
15971                 inputblock.cn.push({
15972                     tag :'span',
15973                     cls : 'input-group-addon',
15974                     html : this.before
15975                 });
15976             }
15977             inputblock.cn.push(input);
15978             if (this.after) {
15979                 inputblock.cn.push({
15980                     tag :'span',
15981                     cls : 'input-group-addon',
15982                     html : this.after
15983                 });
15984             }
15985             
15986         };
15987         
15988         if (align ==='left' && this.fieldLabel.length) {
15989                 Roo.log("left and has label");
15990                 cfg.cn = [
15991                     
15992                     {
15993                         tag: 'label',
15994                         'for' :  id,
15995                         cls : 'control-label col-md-' + this.labelWidth,
15996                         html : this.fieldLabel
15997                         
15998                     },
15999                     {
16000                         cls : "col-md-" + (12 - this.labelWidth), 
16001                         cn: [
16002                             inputblock
16003                         ]
16004                     }
16005                     
16006                 ];
16007         } else if ( this.fieldLabel.length) {
16008                 Roo.log(" label");
16009                  cfg.cn = [
16010                    
16011                     {
16012                         tag: 'label',
16013                         'for': id,
16014                         cls: 'control-label box-input-label',
16015                         //cls : 'input-group-addon',
16016                         html : this.fieldLabel
16017                         
16018                     },
16019                     
16020                     inputblock
16021                     
16022                 ];
16023
16024         } else {
16025             
16026                    Roo.log(" no label && no align");
16027                 cfg.cn = [
16028                     
16029                         inputblock
16030                     
16031                 ];
16032                 
16033                 
16034         };
16035         
16036         if(this.boxLabel){
16037             cfg.cn.push({
16038                 tag: 'label',
16039                 'for': id,
16040                 cls: 'box-label',
16041                 html: this.boxLabel
16042             })
16043         }
16044         
16045         return cfg;
16046         
16047     },
16048     inputEl: function ()
16049     {
16050         return this.el.select('input.roo-radio',true).first();
16051     },
16052     onClick : function()
16053     {   
16054         this.setChecked(true);
16055     },
16056     
16057     setChecked : function(state,suppressEvent)
16058     {
16059         if(state){
16060             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16061                 v.dom.checked = false;
16062             });
16063         }
16064         
16065         this.checked = state;
16066         this.inputEl().dom.checked = state;
16067         
16068         if(suppressEvent !== true){
16069             this.fireEvent('check', this, state);
16070         }
16071         
16072         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
16073         
16074     },
16075     
16076     getGroupValue : function()
16077     {
16078         var value = ''
16079         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16080             if(v.dom.checked == true){
16081                 value = v.dom.value;
16082             }
16083         });
16084         
16085         return value;
16086     },
16087     
16088     /**
16089      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
16090      * @return {Mixed} value The field value
16091      */
16092     getValue : function(){
16093         return this.getGroupValue();
16094     }
16095     
16096 });
16097
16098  
16099 //<script type="text/javascript">
16100
16101 /*
16102  * Based  Ext JS Library 1.1.1
16103  * Copyright(c) 2006-2007, Ext JS, LLC.
16104  * LGPL
16105  *
16106  */
16107  
16108 /**
16109  * @class Roo.HtmlEditorCore
16110  * @extends Roo.Component
16111  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
16112  *
16113  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
16114  */
16115
16116 Roo.HtmlEditorCore = function(config){
16117     
16118     
16119     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
16120     this.addEvents({
16121         /**
16122          * @event initialize
16123          * Fires when the editor is fully initialized (including the iframe)
16124          * @param {Roo.HtmlEditorCore} this
16125          */
16126         initialize: true,
16127         /**
16128          * @event activate
16129          * Fires when the editor is first receives the focus. Any insertion must wait
16130          * until after this event.
16131          * @param {Roo.HtmlEditorCore} this
16132          */
16133         activate: true,
16134          /**
16135          * @event beforesync
16136          * Fires before the textarea is updated with content from the editor iframe. Return false
16137          * to cancel the sync.
16138          * @param {Roo.HtmlEditorCore} this
16139          * @param {String} html
16140          */
16141         beforesync: true,
16142          /**
16143          * @event beforepush
16144          * Fires before the iframe editor is updated with content from the textarea. Return false
16145          * to cancel the push.
16146          * @param {Roo.HtmlEditorCore} this
16147          * @param {String} html
16148          */
16149         beforepush: true,
16150          /**
16151          * @event sync
16152          * Fires when the textarea is updated with content from the editor iframe.
16153          * @param {Roo.HtmlEditorCore} this
16154          * @param {String} html
16155          */
16156         sync: true,
16157          /**
16158          * @event push
16159          * Fires when the iframe editor is updated with content from the textarea.
16160          * @param {Roo.HtmlEditorCore} this
16161          * @param {String} html
16162          */
16163         push: true,
16164         
16165         /**
16166          * @event editorevent
16167          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
16168          * @param {Roo.HtmlEditorCore} this
16169          */
16170         editorevent: true
16171     });
16172      
16173 };
16174
16175
16176 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
16177
16178
16179      /**
16180      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
16181      */
16182     
16183     owner : false,
16184     
16185      /**
16186      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
16187      *                        Roo.resizable.
16188      */
16189     resizable : false,
16190      /**
16191      * @cfg {Number} height (in pixels)
16192      */   
16193     height: 300,
16194    /**
16195      * @cfg {Number} width (in pixels)
16196      */   
16197     width: 500,
16198     
16199     /**
16200      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
16201      * 
16202      */
16203     stylesheets: false,
16204     
16205     // id of frame..
16206     frameId: false,
16207     
16208     // private properties
16209     validationEvent : false,
16210     deferHeight: true,
16211     initialized : false,
16212     activated : false,
16213     sourceEditMode : false,
16214     onFocus : Roo.emptyFn,
16215     iframePad:3,
16216     hideMode:'offsets',
16217     
16218     clearUp: true,
16219     
16220      
16221     
16222
16223     /**
16224      * Protected method that will not generally be called directly. It
16225      * is called when the editor initializes the iframe with HTML contents. Override this method if you
16226      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
16227      */
16228     getDocMarkup : function(){
16229         // body styles..
16230         var st = '';
16231         Roo.log(this.stylesheets);
16232         
16233         // inherit styels from page...?? 
16234         if (this.stylesheets === false) {
16235             
16236             Roo.get(document.head).select('style').each(function(node) {
16237                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16238             });
16239             
16240             Roo.get(document.head).select('link').each(function(node) { 
16241                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16242             });
16243             
16244         } else if (!this.stylesheets.length) {
16245                 // simple..
16246                 st = '<style type="text/css">' +
16247                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16248                    '</style>';
16249         } else {
16250             Roo.each(this.stylesheets, function(s) {
16251                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
16252             });
16253             
16254         }
16255         
16256         st +=  '<style type="text/css">' +
16257             'IMG { cursor: pointer } ' +
16258         '</style>';
16259
16260         
16261         return '<html><head>' + st  +
16262             //<style type="text/css">' +
16263             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16264             //'</style>' +
16265             ' </head><body class="roo-htmleditor-body"></body></html>';
16266     },
16267
16268     // private
16269     onRender : function(ct, position)
16270     {
16271         var _t = this;
16272         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
16273         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
16274         
16275         
16276         this.el.dom.style.border = '0 none';
16277         this.el.dom.setAttribute('tabIndex', -1);
16278         this.el.addClass('x-hidden hide');
16279         
16280         
16281         
16282         if(Roo.isIE){ // fix IE 1px bogus margin
16283             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
16284         }
16285        
16286         
16287         this.frameId = Roo.id();
16288         
16289          
16290         
16291         var iframe = this.owner.wrap.createChild({
16292             tag: 'iframe',
16293             cls: 'form-control', // bootstrap..
16294             id: this.frameId,
16295             name: this.frameId,
16296             frameBorder : 'no',
16297             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
16298         }, this.el
16299         );
16300         
16301         
16302         this.iframe = iframe.dom;
16303
16304          this.assignDocWin();
16305         
16306         this.doc.designMode = 'on';
16307        
16308         this.doc.open();
16309         this.doc.write(this.getDocMarkup());
16310         this.doc.close();
16311
16312         
16313         var task = { // must defer to wait for browser to be ready
16314             run : function(){
16315                 //console.log("run task?" + this.doc.readyState);
16316                 this.assignDocWin();
16317                 if(this.doc.body || this.doc.readyState == 'complete'){
16318                     try {
16319                         this.doc.designMode="on";
16320                     } catch (e) {
16321                         return;
16322                     }
16323                     Roo.TaskMgr.stop(task);
16324                     this.initEditor.defer(10, this);
16325                 }
16326             },
16327             interval : 10,
16328             duration: 10000,
16329             scope: this
16330         };
16331         Roo.TaskMgr.start(task);
16332
16333         
16334          
16335     },
16336
16337     // private
16338     onResize : function(w, h)
16339     {
16340          Roo.log('resize: ' +w + ',' + h );
16341         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
16342         if(!this.iframe){
16343             return;
16344         }
16345         if(typeof w == 'number'){
16346             
16347             this.iframe.style.width = w + 'px';
16348         }
16349         if(typeof h == 'number'){
16350             
16351             this.iframe.style.height = h + 'px';
16352             if(this.doc){
16353                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
16354             }
16355         }
16356         
16357     },
16358
16359     /**
16360      * Toggles the editor between standard and source edit mode.
16361      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
16362      */
16363     toggleSourceEdit : function(sourceEditMode){
16364         
16365         this.sourceEditMode = sourceEditMode === true;
16366         
16367         if(this.sourceEditMode){
16368  
16369             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
16370             
16371         }else{
16372             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
16373             //this.iframe.className = '';
16374             this.deferFocus();
16375         }
16376         //this.setSize(this.owner.wrap.getSize());
16377         //this.fireEvent('editmodechange', this, this.sourceEditMode);
16378     },
16379
16380     
16381   
16382
16383     /**
16384      * Protected method that will not generally be called directly. If you need/want
16385      * custom HTML cleanup, this is the method you should override.
16386      * @param {String} html The HTML to be cleaned
16387      * return {String} The cleaned HTML
16388      */
16389     cleanHtml : function(html){
16390         html = String(html);
16391         if(html.length > 5){
16392             if(Roo.isSafari){ // strip safari nonsense
16393                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
16394             }
16395         }
16396         if(html == '&nbsp;'){
16397             html = '';
16398         }
16399         return html;
16400     },
16401
16402     /**
16403      * HTML Editor -> Textarea
16404      * Protected method that will not generally be called directly. Syncs the contents
16405      * of the editor iframe with the textarea.
16406      */
16407     syncValue : function(){
16408         if(this.initialized){
16409             var bd = (this.doc.body || this.doc.documentElement);
16410             //this.cleanUpPaste(); -- this is done else where and causes havoc..
16411             var html = bd.innerHTML;
16412             if(Roo.isSafari){
16413                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
16414                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
16415                 if(m && m[1]){
16416                     html = '<div style="'+m[0]+'">' + html + '</div>';
16417                 }
16418             }
16419             html = this.cleanHtml(html);
16420             // fix up the special chars.. normaly like back quotes in word...
16421             // however we do not want to do this with chinese..
16422             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
16423                 var cc = b.charCodeAt();
16424                 if (
16425                     (cc >= 0x4E00 && cc < 0xA000 ) ||
16426                     (cc >= 0x3400 && cc < 0x4E00 ) ||
16427                     (cc >= 0xf900 && cc < 0xfb00 )
16428                 ) {
16429                         return b;
16430                 }
16431                 return "&#"+cc+";" 
16432             });
16433             if(this.owner.fireEvent('beforesync', this, html) !== false){
16434                 this.el.dom.value = html;
16435                 this.owner.fireEvent('sync', this, html);
16436             }
16437         }
16438     },
16439
16440     /**
16441      * Protected method that will not generally be called directly. Pushes the value of the textarea
16442      * into the iframe editor.
16443      */
16444     pushValue : function(){
16445         if(this.initialized){
16446             var v = this.el.dom.value.trim();
16447             
16448 //            if(v.length < 1){
16449 //                v = '&#160;';
16450 //            }
16451             
16452             if(this.owner.fireEvent('beforepush', this, v) !== false){
16453                 var d = (this.doc.body || this.doc.documentElement);
16454                 d.innerHTML = v;
16455                 this.cleanUpPaste();
16456                 this.el.dom.value = d.innerHTML;
16457                 this.owner.fireEvent('push', this, v);
16458             }
16459         }
16460     },
16461
16462     // private
16463     deferFocus : function(){
16464         this.focus.defer(10, this);
16465     },
16466
16467     // doc'ed in Field
16468     focus : function(){
16469         if(this.win && !this.sourceEditMode){
16470             this.win.focus();
16471         }else{
16472             this.el.focus();
16473         }
16474     },
16475     
16476     assignDocWin: function()
16477     {
16478         var iframe = this.iframe;
16479         
16480          if(Roo.isIE){
16481             this.doc = iframe.contentWindow.document;
16482             this.win = iframe.contentWindow;
16483         } else {
16484             if (!Roo.get(this.frameId)) {
16485                 return;
16486             }
16487             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
16488             this.win = Roo.get(this.frameId).dom.contentWindow;
16489         }
16490     },
16491     
16492     // private
16493     initEditor : function(){
16494         //console.log("INIT EDITOR");
16495         this.assignDocWin();
16496         
16497         
16498         
16499         this.doc.designMode="on";
16500         this.doc.open();
16501         this.doc.write(this.getDocMarkup());
16502         this.doc.close();
16503         
16504         var dbody = (this.doc.body || this.doc.documentElement);
16505         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
16506         // this copies styles from the containing element into thsi one..
16507         // not sure why we need all of this..
16508         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
16509         
16510         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
16511         //ss['background-attachment'] = 'fixed'; // w3c
16512         dbody.bgProperties = 'fixed'; // ie
16513         //Roo.DomHelper.applyStyles(dbody, ss);
16514         Roo.EventManager.on(this.doc, {
16515             //'mousedown': this.onEditorEvent,
16516             'mouseup': this.onEditorEvent,
16517             'dblclick': this.onEditorEvent,
16518             'click': this.onEditorEvent,
16519             'keyup': this.onEditorEvent,
16520             buffer:100,
16521             scope: this
16522         });
16523         if(Roo.isGecko){
16524             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
16525         }
16526         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
16527             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
16528         }
16529         this.initialized = true;
16530
16531         this.owner.fireEvent('initialize', this);
16532         this.pushValue();
16533     },
16534
16535     // private
16536     onDestroy : function(){
16537         
16538         
16539         
16540         if(this.rendered){
16541             
16542             //for (var i =0; i < this.toolbars.length;i++) {
16543             //    // fixme - ask toolbars for heights?
16544             //    this.toolbars[i].onDestroy();
16545            // }
16546             
16547             //this.wrap.dom.innerHTML = '';
16548             //this.wrap.remove();
16549         }
16550     },
16551
16552     // private
16553     onFirstFocus : function(){
16554         
16555         this.assignDocWin();
16556         
16557         
16558         this.activated = true;
16559          
16560     
16561         if(Roo.isGecko){ // prevent silly gecko errors
16562             this.win.focus();
16563             var s = this.win.getSelection();
16564             if(!s.focusNode || s.focusNode.nodeType != 3){
16565                 var r = s.getRangeAt(0);
16566                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
16567                 r.collapse(true);
16568                 this.deferFocus();
16569             }
16570             try{
16571                 this.execCmd('useCSS', true);
16572                 this.execCmd('styleWithCSS', false);
16573             }catch(e){}
16574         }
16575         this.owner.fireEvent('activate', this);
16576     },
16577
16578     // private
16579     adjustFont: function(btn){
16580         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
16581         //if(Roo.isSafari){ // safari
16582         //    adjust *= 2;
16583        // }
16584         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
16585         if(Roo.isSafari){ // safari
16586             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
16587             v =  (v < 10) ? 10 : v;
16588             v =  (v > 48) ? 48 : v;
16589             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
16590             
16591         }
16592         
16593         
16594         v = Math.max(1, v+adjust);
16595         
16596         this.execCmd('FontSize', v  );
16597     },
16598
16599     onEditorEvent : function(e){
16600         this.owner.fireEvent('editorevent', this, e);
16601       //  this.updateToolbar();
16602         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
16603     },
16604
16605     insertTag : function(tg)
16606     {
16607         // could be a bit smarter... -> wrap the current selected tRoo..
16608         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
16609             
16610             range = this.createRange(this.getSelection());
16611             var wrappingNode = this.doc.createElement(tg.toLowerCase());
16612             wrappingNode.appendChild(range.extractContents());
16613             range.insertNode(wrappingNode);
16614
16615             return;
16616             
16617             
16618             
16619         }
16620         this.execCmd("formatblock",   tg);
16621         
16622     },
16623     
16624     insertText : function(txt)
16625     {
16626         
16627         
16628         var range = this.createRange();
16629         range.deleteContents();
16630                //alert(Sender.getAttribute('label'));
16631                
16632         range.insertNode(this.doc.createTextNode(txt));
16633     } ,
16634     
16635      
16636
16637     /**
16638      * Executes a Midas editor command on the editor document and performs necessary focus and
16639      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
16640      * @param {String} cmd The Midas command
16641      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16642      */
16643     relayCmd : function(cmd, value){
16644         this.win.focus();
16645         this.execCmd(cmd, value);
16646         this.owner.fireEvent('editorevent', this);
16647         //this.updateToolbar();
16648         this.owner.deferFocus();
16649     },
16650
16651     /**
16652      * Executes a Midas editor command directly on the editor document.
16653      * For visual commands, you should use {@link #relayCmd} instead.
16654      * <b>This should only be called after the editor is initialized.</b>
16655      * @param {String} cmd The Midas command
16656      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16657      */
16658     execCmd : function(cmd, value){
16659         this.doc.execCommand(cmd, false, value === undefined ? null : value);
16660         this.syncValue();
16661     },
16662  
16663  
16664    
16665     /**
16666      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
16667      * to insert tRoo.
16668      * @param {String} text | dom node.. 
16669      */
16670     insertAtCursor : function(text)
16671     {
16672         
16673         
16674         
16675         if(!this.activated){
16676             return;
16677         }
16678         /*
16679         if(Roo.isIE){
16680             this.win.focus();
16681             var r = this.doc.selection.createRange();
16682             if(r){
16683                 r.collapse(true);
16684                 r.pasteHTML(text);
16685                 this.syncValue();
16686                 this.deferFocus();
16687             
16688             }
16689             return;
16690         }
16691         */
16692         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
16693             this.win.focus();
16694             
16695             
16696             // from jquery ui (MIT licenced)
16697             var range, node;
16698             var win = this.win;
16699             
16700             if (win.getSelection && win.getSelection().getRangeAt) {
16701                 range = win.getSelection().getRangeAt(0);
16702                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
16703                 range.insertNode(node);
16704             } else if (win.document.selection && win.document.selection.createRange) {
16705                 // no firefox support
16706                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
16707                 win.document.selection.createRange().pasteHTML(txt);
16708             } else {
16709                 // no firefox support
16710                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
16711                 this.execCmd('InsertHTML', txt);
16712             } 
16713             
16714             this.syncValue();
16715             
16716             this.deferFocus();
16717         }
16718     },
16719  // private
16720     mozKeyPress : function(e){
16721         if(e.ctrlKey){
16722             var c = e.getCharCode(), cmd;
16723           
16724             if(c > 0){
16725                 c = String.fromCharCode(c).toLowerCase();
16726                 switch(c){
16727                     case 'b':
16728                         cmd = 'bold';
16729                         break;
16730                     case 'i':
16731                         cmd = 'italic';
16732                         break;
16733                     
16734                     case 'u':
16735                         cmd = 'underline';
16736                         break;
16737                     
16738                     case 'v':
16739                         this.cleanUpPaste.defer(100, this);
16740                         return;
16741                         
16742                 }
16743                 if(cmd){
16744                     this.win.focus();
16745                     this.execCmd(cmd);
16746                     this.deferFocus();
16747                     e.preventDefault();
16748                 }
16749                 
16750             }
16751         }
16752     },
16753
16754     // private
16755     fixKeys : function(){ // load time branching for fastest keydown performance
16756         if(Roo.isIE){
16757             return function(e){
16758                 var k = e.getKey(), r;
16759                 if(k == e.TAB){
16760                     e.stopEvent();
16761                     r = this.doc.selection.createRange();
16762                     if(r){
16763                         r.collapse(true);
16764                         r.pasteHTML('&#160;&#160;&#160;&#160;');
16765                         this.deferFocus();
16766                     }
16767                     return;
16768                 }
16769                 
16770                 if(k == e.ENTER){
16771                     r = this.doc.selection.createRange();
16772                     if(r){
16773                         var target = r.parentElement();
16774                         if(!target || target.tagName.toLowerCase() != 'li'){
16775                             e.stopEvent();
16776                             r.pasteHTML('<br />');
16777                             r.collapse(false);
16778                             r.select();
16779                         }
16780                     }
16781                 }
16782                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
16783                     this.cleanUpPaste.defer(100, this);
16784                     return;
16785                 }
16786                 
16787                 
16788             };
16789         }else if(Roo.isOpera){
16790             return function(e){
16791                 var k = e.getKey();
16792                 if(k == e.TAB){
16793                     e.stopEvent();
16794                     this.win.focus();
16795                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
16796                     this.deferFocus();
16797                 }
16798                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
16799                     this.cleanUpPaste.defer(100, this);
16800                     return;
16801                 }
16802                 
16803             };
16804         }else if(Roo.isSafari){
16805             return function(e){
16806                 var k = e.getKey();
16807                 
16808                 if(k == e.TAB){
16809                     e.stopEvent();
16810                     this.execCmd('InsertText','\t');
16811                     this.deferFocus();
16812                     return;
16813                 }
16814                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
16815                     this.cleanUpPaste.defer(100, this);
16816                     return;
16817                 }
16818                 
16819              };
16820         }
16821     }(),
16822     
16823     getAllAncestors: function()
16824     {
16825         var p = this.getSelectedNode();
16826         var a = [];
16827         if (!p) {
16828             a.push(p); // push blank onto stack..
16829             p = this.getParentElement();
16830         }
16831         
16832         
16833         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
16834             a.push(p);
16835             p = p.parentNode;
16836         }
16837         a.push(this.doc.body);
16838         return a;
16839     },
16840     lastSel : false,
16841     lastSelNode : false,
16842     
16843     
16844     getSelection : function() 
16845     {
16846         this.assignDocWin();
16847         return Roo.isIE ? this.doc.selection : this.win.getSelection();
16848     },
16849     
16850     getSelectedNode: function() 
16851     {
16852         // this may only work on Gecko!!!
16853         
16854         // should we cache this!!!!
16855         
16856         
16857         
16858          
16859         var range = this.createRange(this.getSelection()).cloneRange();
16860         
16861         if (Roo.isIE) {
16862             var parent = range.parentElement();
16863             while (true) {
16864                 var testRange = range.duplicate();
16865                 testRange.moveToElementText(parent);
16866                 if (testRange.inRange(range)) {
16867                     break;
16868                 }
16869                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
16870                     break;
16871                 }
16872                 parent = parent.parentElement;
16873             }
16874             return parent;
16875         }
16876         
16877         // is ancestor a text element.
16878         var ac =  range.commonAncestorContainer;
16879         if (ac.nodeType == 3) {
16880             ac = ac.parentNode;
16881         }
16882         
16883         var ar = ac.childNodes;
16884          
16885         var nodes = [];
16886         var other_nodes = [];
16887         var has_other_nodes = false;
16888         for (var i=0;i<ar.length;i++) {
16889             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
16890                 continue;
16891             }
16892             // fullly contained node.
16893             
16894             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
16895                 nodes.push(ar[i]);
16896                 continue;
16897             }
16898             
16899             // probably selected..
16900             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
16901                 other_nodes.push(ar[i]);
16902                 continue;
16903             }
16904             // outer..
16905             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
16906                 continue;
16907             }
16908             
16909             
16910             has_other_nodes = true;
16911         }
16912         if (!nodes.length && other_nodes.length) {
16913             nodes= other_nodes;
16914         }
16915         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
16916             return false;
16917         }
16918         
16919         return nodes[0];
16920     },
16921     createRange: function(sel)
16922     {
16923         // this has strange effects when using with 
16924         // top toolbar - not sure if it's a great idea.
16925         //this.editor.contentWindow.focus();
16926         if (typeof sel != "undefined") {
16927             try {
16928                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
16929             } catch(e) {
16930                 return this.doc.createRange();
16931             }
16932         } else {
16933             return this.doc.createRange();
16934         }
16935     },
16936     getParentElement: function()
16937     {
16938         
16939         this.assignDocWin();
16940         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
16941         
16942         var range = this.createRange(sel);
16943          
16944         try {
16945             var p = range.commonAncestorContainer;
16946             while (p.nodeType == 3) { // text node
16947                 p = p.parentNode;
16948             }
16949             return p;
16950         } catch (e) {
16951             return null;
16952         }
16953     
16954     },
16955     /***
16956      *
16957      * Range intersection.. the hard stuff...
16958      *  '-1' = before
16959      *  '0' = hits..
16960      *  '1' = after.
16961      *         [ -- selected range --- ]
16962      *   [fail]                        [fail]
16963      *
16964      *    basically..
16965      *      if end is before start or  hits it. fail.
16966      *      if start is after end or hits it fail.
16967      *
16968      *   if either hits (but other is outside. - then it's not 
16969      *   
16970      *    
16971      **/
16972     
16973     
16974     // @see http://www.thismuchiknow.co.uk/?p=64.
16975     rangeIntersectsNode : function(range, node)
16976     {
16977         var nodeRange = node.ownerDocument.createRange();
16978         try {
16979             nodeRange.selectNode(node);
16980         } catch (e) {
16981             nodeRange.selectNodeContents(node);
16982         }
16983     
16984         var rangeStartRange = range.cloneRange();
16985         rangeStartRange.collapse(true);
16986     
16987         var rangeEndRange = range.cloneRange();
16988         rangeEndRange.collapse(false);
16989     
16990         var nodeStartRange = nodeRange.cloneRange();
16991         nodeStartRange.collapse(true);
16992     
16993         var nodeEndRange = nodeRange.cloneRange();
16994         nodeEndRange.collapse(false);
16995     
16996         return rangeStartRange.compareBoundaryPoints(
16997                  Range.START_TO_START, nodeEndRange) == -1 &&
16998                rangeEndRange.compareBoundaryPoints(
16999                  Range.START_TO_START, nodeStartRange) == 1;
17000         
17001          
17002     },
17003     rangeCompareNode : function(range, node)
17004     {
17005         var nodeRange = node.ownerDocument.createRange();
17006         try {
17007             nodeRange.selectNode(node);
17008         } catch (e) {
17009             nodeRange.selectNodeContents(node);
17010         }
17011         
17012         
17013         range.collapse(true);
17014     
17015         nodeRange.collapse(true);
17016      
17017         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
17018         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
17019          
17020         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
17021         
17022         var nodeIsBefore   =  ss == 1;
17023         var nodeIsAfter    = ee == -1;
17024         
17025         if (nodeIsBefore && nodeIsAfter)
17026             return 0; // outer
17027         if (!nodeIsBefore && nodeIsAfter)
17028             return 1; //right trailed.
17029         
17030         if (nodeIsBefore && !nodeIsAfter)
17031             return 2;  // left trailed.
17032         // fully contined.
17033         return 3;
17034     },
17035
17036     // private? - in a new class?
17037     cleanUpPaste :  function()
17038     {
17039         // cleans up the whole document..
17040         Roo.log('cleanuppaste');
17041         
17042         this.cleanUpChildren(this.doc.body);
17043         var clean = this.cleanWordChars(this.doc.body.innerHTML);
17044         if (clean != this.doc.body.innerHTML) {
17045             this.doc.body.innerHTML = clean;
17046         }
17047         
17048     },
17049     
17050     cleanWordChars : function(input) {// change the chars to hex code
17051         var he = Roo.HtmlEditorCore;
17052         
17053         var output = input;
17054         Roo.each(he.swapCodes, function(sw) { 
17055             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
17056             
17057             output = output.replace(swapper, sw[1]);
17058         });
17059         
17060         return output;
17061     },
17062     
17063     
17064     cleanUpChildren : function (n)
17065     {
17066         if (!n.childNodes.length) {
17067             return;
17068         }
17069         for (var i = n.childNodes.length-1; i > -1 ; i--) {
17070            this.cleanUpChild(n.childNodes[i]);
17071         }
17072     },
17073     
17074     
17075         
17076     
17077     cleanUpChild : function (node)
17078     {
17079         var ed = this;
17080         //console.log(node);
17081         if (node.nodeName == "#text") {
17082             // clean up silly Windows -- stuff?
17083             return; 
17084         }
17085         if (node.nodeName == "#comment") {
17086             node.parentNode.removeChild(node);
17087             // clean up silly Windows -- stuff?
17088             return; 
17089         }
17090         
17091         if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1 && this.clearUp) {
17092             // remove node.
17093             node.parentNode.removeChild(node);
17094             return;
17095             
17096         }
17097         
17098         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
17099         
17100         // remove <a name=....> as rendering on yahoo mailer is borked with this.
17101         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
17102         
17103         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
17104         //    remove_keep_children = true;
17105         //}
17106         
17107         if (remove_keep_children) {
17108             this.cleanUpChildren(node);
17109             // inserts everything just before this node...
17110             while (node.childNodes.length) {
17111                 var cn = node.childNodes[0];
17112                 node.removeChild(cn);
17113                 node.parentNode.insertBefore(cn, node);
17114             }
17115             node.parentNode.removeChild(node);
17116             return;
17117         }
17118         
17119         if (!node.attributes || !node.attributes.length) {
17120             this.cleanUpChildren(node);
17121             return;
17122         }
17123         
17124         function cleanAttr(n,v)
17125         {
17126             
17127             if (v.match(/^\./) || v.match(/^\//)) {
17128                 return;
17129             }
17130             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
17131                 return;
17132             }
17133             if (v.match(/^#/)) {
17134                 return;
17135             }
17136 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
17137             node.removeAttribute(n);
17138             
17139         }
17140         
17141         function cleanStyle(n,v)
17142         {
17143             if (v.match(/expression/)) { //XSS?? should we even bother..
17144                 node.removeAttribute(n);
17145                 return;
17146             }
17147             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
17148             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
17149             
17150             
17151             var parts = v.split(/;/);
17152             var clean = [];
17153             
17154             Roo.each(parts, function(p) {
17155                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
17156                 if (!p.length) {
17157                     return true;
17158                 }
17159                 var l = p.split(':').shift().replace(/\s+/g,'');
17160                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
17161                 
17162                 if ( cblack.indexOf(l) > -1) {
17163 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17164                     //node.removeAttribute(n);
17165                     return true;
17166                 }
17167                 //Roo.log()
17168                 // only allow 'c whitelisted system attributes'
17169                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
17170 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17171                     //node.removeAttribute(n);
17172                     return true;
17173                 }
17174                 
17175                 
17176                  
17177                 
17178                 clean.push(p);
17179                 return true;
17180             });
17181             if (clean.length) { 
17182                 node.setAttribute(n, clean.join(';'));
17183             } else {
17184                 node.removeAttribute(n);
17185             }
17186             
17187         }
17188         
17189         
17190         for (var i = node.attributes.length-1; i > -1 ; i--) {
17191             var a = node.attributes[i];
17192             //console.log(a);
17193             
17194             if (a.name.toLowerCase().substr(0,2)=='on')  {
17195                 node.removeAttribute(a.name);
17196                 continue;
17197             }
17198             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
17199                 node.removeAttribute(a.name);
17200                 continue;
17201             }
17202             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
17203                 cleanAttr(a.name,a.value); // fixme..
17204                 continue;
17205             }
17206             if (a.name == 'style') {
17207                 cleanStyle(a.name,a.value);
17208                 continue;
17209             }
17210             /// clean up MS crap..
17211             // tecnically this should be a list of valid class'es..
17212             
17213             
17214             if (a.name == 'class') {
17215                 if (a.value.match(/^Mso/)) {
17216                     node.className = '';
17217                 }
17218                 
17219                 if (a.value.match(/body/)) {
17220                     node.className = '';
17221                 }
17222                 continue;
17223             }
17224             
17225             // style cleanup!?
17226             // class cleanup?
17227             
17228         }
17229         
17230         
17231         this.cleanUpChildren(node);
17232         
17233         
17234     },
17235     /**
17236      * Clean up MS wordisms...
17237      */
17238     cleanWord : function(node)
17239     {
17240         var _t = this;
17241         var cleanWordChildren = function()
17242         {
17243             if (!node.childNodes.length) {
17244                 return;
17245             }
17246             for (var i = node.childNodes.length-1; i > -1 ; i--) {
17247                _t.cleanWord(node.childNodes[i]);
17248             }
17249         }
17250         
17251         
17252         if (!node) {
17253             this.cleanWord(this.doc.body);
17254             return;
17255         }
17256         if (node.nodeName == "#text") {
17257             // clean up silly Windows -- stuff?
17258             return; 
17259         }
17260         if (node.nodeName == "#comment") {
17261             node.parentNode.removeChild(node);
17262             // clean up silly Windows -- stuff?
17263             return; 
17264         }
17265         
17266         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
17267             node.parentNode.removeChild(node);
17268             return;
17269         }
17270         
17271         // remove - but keep children..
17272         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
17273             while (node.childNodes.length) {
17274                 var cn = node.childNodes[0];
17275                 node.removeChild(cn);
17276                 node.parentNode.insertBefore(cn, node);
17277             }
17278             node.parentNode.removeChild(node);
17279             cleanWordChildren();
17280             return;
17281         }
17282         // clean styles
17283         if (node.className.length) {
17284             
17285             var cn = node.className.split(/\W+/);
17286             var cna = [];
17287             Roo.each(cn, function(cls) {
17288                 if (cls.match(/Mso[a-zA-Z]+/)) {
17289                     return;
17290                 }
17291                 cna.push(cls);
17292             });
17293             node.className = cna.length ? cna.join(' ') : '';
17294             if (!cna.length) {
17295                 node.removeAttribute("class");
17296             }
17297         }
17298         
17299         if (node.hasAttribute("lang")) {
17300             node.removeAttribute("lang");
17301         }
17302         
17303         if (node.hasAttribute("style")) {
17304             
17305             var styles = node.getAttribute("style").split(";");
17306             var nstyle = [];
17307             Roo.each(styles, function(s) {
17308                 if (!s.match(/:/)) {
17309                     return;
17310                 }
17311                 var kv = s.split(":");
17312                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
17313                     return;
17314                 }
17315                 // what ever is left... we allow.
17316                 nstyle.push(s);
17317             });
17318             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
17319             if (!nstyle.length) {
17320                 node.removeAttribute('style');
17321             }
17322         }
17323         
17324         cleanWordChildren();
17325         
17326         
17327     },
17328     domToHTML : function(currentElement, depth, nopadtext) {
17329         
17330             depth = depth || 0;
17331             nopadtext = nopadtext || false;
17332         
17333             if (!currentElement) {
17334                 return this.domToHTML(this.doc.body);
17335             }
17336             
17337             //Roo.log(currentElement);
17338             var j;
17339             var allText = false;
17340             var nodeName = currentElement.nodeName;
17341             var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
17342             
17343             if  (nodeName == '#text') {
17344                 return currentElement.nodeValue;
17345             }
17346             
17347             
17348             var ret = '';
17349             if (nodeName != 'BODY') {
17350                  
17351                 var i = 0;
17352                 // Prints the node tagName, such as <A>, <IMG>, etc
17353                 if (tagName) {
17354                     var attr = [];
17355                     for(i = 0; i < currentElement.attributes.length;i++) {
17356                         // quoting?
17357                         var aname = currentElement.attributes.item(i).name;
17358                         if (!currentElement.attributes.item(i).value.length) {
17359                             continue;
17360                         }
17361                         attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
17362                     }
17363                     
17364                     ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
17365                 } 
17366                 else {
17367                     
17368                     // eack
17369                 }
17370             } else {
17371                 tagName = false;
17372             }
17373             if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
17374                 return ret;
17375             }
17376             if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
17377                 nopadtext = true;
17378             }
17379             
17380             
17381             // Traverse the tree
17382             i = 0;
17383             var currentElementChild = currentElement.childNodes.item(i);
17384             var allText = true;
17385             var innerHTML  = '';
17386             lastnode = '';
17387             while (currentElementChild) {
17388                 // Formatting code (indent the tree so it looks nice on the screen)
17389                 var nopad = nopadtext;
17390                 if (lastnode == 'SPAN') {
17391                     nopad  = true;
17392                 }
17393                 // text
17394                 if  (currentElementChild.nodeName == '#text') {
17395                     var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
17396                     if (!nopad && toadd.length > 80) {
17397                         innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
17398                     }
17399                     innerHTML  += toadd;
17400                     
17401                     i++;
17402                     currentElementChild = currentElement.childNodes.item(i);
17403                     lastNode = '';
17404                     continue;
17405                 }
17406                 allText = false;
17407                 
17408                 innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
17409                     
17410                 // Recursively traverse the tree structure of the child node
17411                 innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
17412                 lastnode = currentElementChild.nodeName;
17413                 i++;
17414                 currentElementChild=currentElement.childNodes.item(i);
17415             }
17416             
17417             ret += innerHTML;
17418             
17419             if (!allText) {
17420                     // The remaining code is mostly for formatting the tree
17421                 ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
17422             }
17423             
17424             
17425             if (tagName) {
17426                 ret+= "</"+tagName+">";
17427             }
17428             return ret;
17429             
17430         }
17431     
17432     // hide stuff that is not compatible
17433     /**
17434      * @event blur
17435      * @hide
17436      */
17437     /**
17438      * @event change
17439      * @hide
17440      */
17441     /**
17442      * @event focus
17443      * @hide
17444      */
17445     /**
17446      * @event specialkey
17447      * @hide
17448      */
17449     /**
17450      * @cfg {String} fieldClass @hide
17451      */
17452     /**
17453      * @cfg {String} focusClass @hide
17454      */
17455     /**
17456      * @cfg {String} autoCreate @hide
17457      */
17458     /**
17459      * @cfg {String} inputType @hide
17460      */
17461     /**
17462      * @cfg {String} invalidClass @hide
17463      */
17464     /**
17465      * @cfg {String} invalidText @hide
17466      */
17467     /**
17468      * @cfg {String} msgFx @hide
17469      */
17470     /**
17471      * @cfg {String} validateOnBlur @hide
17472      */
17473 });
17474
17475 Roo.HtmlEditorCore.white = [
17476         'area', 'br', 'img', 'input', 'hr', 'wbr',
17477         
17478        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
17479        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
17480        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
17481        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
17482        'table',   'ul',         'xmp', 
17483        
17484        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
17485       'thead',   'tr', 
17486      
17487       'dir', 'menu', 'ol', 'ul', 'dl',
17488        
17489       'embed',  'object'
17490 ];
17491
17492
17493 Roo.HtmlEditorCore.black = [
17494     //    'embed',  'object', // enable - backend responsiblity to clean thiese
17495         'applet', // 
17496         'base',   'basefont', 'bgsound', 'blink',  'body', 
17497         'frame',  'frameset', 'head',    'html',   'ilayer', 
17498         'iframe', 'layer',  'link',     'meta',    'object',   
17499         'script', 'style' ,'title',  'xml' // clean later..
17500 ];
17501 Roo.HtmlEditorCore.clean = [
17502     'script', 'style', 'title', 'xml'
17503 ];
17504 Roo.HtmlEditorCore.remove = [
17505     'font'
17506 ];
17507 // attributes..
17508
17509 Roo.HtmlEditorCore.ablack = [
17510     'on'
17511 ];
17512     
17513 Roo.HtmlEditorCore.aclean = [ 
17514     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
17515 ];
17516
17517 // protocols..
17518 Roo.HtmlEditorCore.pwhite= [
17519         'http',  'https',  'mailto'
17520 ];
17521
17522 // white listed style attributes.
17523 Roo.HtmlEditorCore.cwhite= [
17524       //  'text-align', /// default is to allow most things..
17525       
17526          
17527 //        'font-size'//??
17528 ];
17529
17530 // black listed style attributes.
17531 Roo.HtmlEditorCore.cblack= [
17532       //  'font-size' -- this can be set by the project 
17533 ];
17534
17535
17536 Roo.HtmlEditorCore.swapCodes   =[ 
17537     [    8211, "--" ], 
17538     [    8212, "--" ], 
17539     [    8216,  "'" ],  
17540     [    8217, "'" ],  
17541     [    8220, '"' ],  
17542     [    8221, '"' ],  
17543     [    8226, "*" ],  
17544     [    8230, "..." ]
17545 ]; 
17546
17547     /*
17548  * - LGPL
17549  *
17550  * HtmlEditor
17551  * 
17552  */
17553
17554 /**
17555  * @class Roo.bootstrap.HtmlEditor
17556  * @extends Roo.bootstrap.TextArea
17557  * Bootstrap HtmlEditor class
17558
17559  * @constructor
17560  * Create a new HtmlEditor
17561  * @param {Object} config The config object
17562  */
17563
17564 Roo.bootstrap.HtmlEditor = function(config){
17565     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
17566     if (!this.toolbars) {
17567         this.toolbars = [];
17568     }
17569     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
17570     this.addEvents({
17571             /**
17572              * @event initialize
17573              * Fires when the editor is fully initialized (including the iframe)
17574              * @param {HtmlEditor} this
17575              */
17576             initialize: true,
17577             /**
17578              * @event activate
17579              * Fires when the editor is first receives the focus. Any insertion must wait
17580              * until after this event.
17581              * @param {HtmlEditor} this
17582              */
17583             activate: true,
17584              /**
17585              * @event beforesync
17586              * Fires before the textarea is updated with content from the editor iframe. Return false
17587              * to cancel the sync.
17588              * @param {HtmlEditor} this
17589              * @param {String} html
17590              */
17591             beforesync: true,
17592              /**
17593              * @event beforepush
17594              * Fires before the iframe editor is updated with content from the textarea. Return false
17595              * to cancel the push.
17596              * @param {HtmlEditor} this
17597              * @param {String} html
17598              */
17599             beforepush: true,
17600              /**
17601              * @event sync
17602              * Fires when the textarea is updated with content from the editor iframe.
17603              * @param {HtmlEditor} this
17604              * @param {String} html
17605              */
17606             sync: true,
17607              /**
17608              * @event push
17609              * Fires when the iframe editor is updated with content from the textarea.
17610              * @param {HtmlEditor} this
17611              * @param {String} html
17612              */
17613             push: true,
17614              /**
17615              * @event editmodechange
17616              * Fires when the editor switches edit modes
17617              * @param {HtmlEditor} this
17618              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
17619              */
17620             editmodechange: true,
17621             /**
17622              * @event editorevent
17623              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
17624              * @param {HtmlEditor} this
17625              */
17626             editorevent: true,
17627             /**
17628              * @event firstfocus
17629              * Fires when on first focus - needed by toolbars..
17630              * @param {HtmlEditor} this
17631              */
17632             firstfocus: true,
17633             /**
17634              * @event autosave
17635              * Auto save the htmlEditor value as a file into Events
17636              * @param {HtmlEditor} this
17637              */
17638             autosave: true,
17639             /**
17640              * @event savedpreview
17641              * preview the saved version of htmlEditor
17642              * @param {HtmlEditor} this
17643              */
17644             savedpreview: true
17645         });
17646 };
17647
17648
17649 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
17650     
17651     
17652       /**
17653      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
17654      */
17655     toolbars : false,
17656    
17657      /**
17658      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
17659      *                        Roo.resizable.
17660      */
17661     resizable : false,
17662      /**
17663      * @cfg {Number} height (in pixels)
17664      */   
17665     height: 300,
17666    /**
17667      * @cfg {Number} width (in pixels)
17668      */   
17669     width: false,
17670     
17671     /**
17672      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
17673      * 
17674      */
17675     stylesheets: false,
17676     
17677     // id of frame..
17678     frameId: false,
17679     
17680     // private properties
17681     validationEvent : false,
17682     deferHeight: true,
17683     initialized : false,
17684     activated : false,
17685     
17686     onFocus : Roo.emptyFn,
17687     iframePad:3,
17688     hideMode:'offsets',
17689     
17690     
17691     tbContainer : false,
17692     
17693     toolbarContainer :function() {
17694         return this.wrap.select('.x-html-editor-tb',true).first();
17695     },
17696
17697     /**
17698      * Protected method that will not generally be called directly. It
17699      * is called when the editor creates its toolbar. Override this method if you need to
17700      * add custom toolbar buttons.
17701      * @param {HtmlEditor} editor
17702      */
17703     createToolbar : function(){
17704         
17705         Roo.log("create toolbars");
17706         
17707         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
17708         this.toolbars[0].render(this.toolbarContainer());
17709         
17710         return;
17711         
17712 //        if (!editor.toolbars || !editor.toolbars.length) {
17713 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
17714 //        }
17715 //        
17716 //        for (var i =0 ; i < editor.toolbars.length;i++) {
17717 //            editor.toolbars[i] = Roo.factory(
17718 //                    typeof(editor.toolbars[i]) == 'string' ?
17719 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
17720 //                Roo.bootstrap.HtmlEditor);
17721 //            editor.toolbars[i].init(editor);
17722 //        }
17723     },
17724
17725      
17726     // private
17727     onRender : function(ct, position)
17728     {
17729        // Roo.log("Call onRender: " + this.xtype);
17730         var _t = this;
17731         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
17732       
17733         this.wrap = this.inputEl().wrap({
17734             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
17735         });
17736         
17737         this.editorcore.onRender(ct, position);
17738          
17739         if (this.resizable) {
17740             this.resizeEl = new Roo.Resizable(this.wrap, {
17741                 pinned : true,
17742                 wrap: true,
17743                 dynamic : true,
17744                 minHeight : this.height,
17745                 height: this.height,
17746                 handles : this.resizable,
17747                 width: this.width,
17748                 listeners : {
17749                     resize : function(r, w, h) {
17750                         _t.onResize(w,h); // -something
17751                     }
17752                 }
17753             });
17754             
17755         }
17756         this.createToolbar(this);
17757        
17758         
17759         if(!this.width && this.resizable){
17760             this.setSize(this.wrap.getSize());
17761         }
17762         if (this.resizeEl) {
17763             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
17764             // should trigger onReize..
17765         }
17766         
17767     },
17768
17769     // private
17770     onResize : function(w, h)
17771     {
17772         Roo.log('resize: ' +w + ',' + h );
17773         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
17774         var ew = false;
17775         var eh = false;
17776         
17777         if(this.inputEl() ){
17778             if(typeof w == 'number'){
17779                 var aw = w - this.wrap.getFrameWidth('lr');
17780                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
17781                 ew = aw;
17782             }
17783             if(typeof h == 'number'){
17784                  var tbh = -11;  // fixme it needs to tool bar size!
17785                 for (var i =0; i < this.toolbars.length;i++) {
17786                     // fixme - ask toolbars for heights?
17787                     tbh += this.toolbars[i].el.getHeight();
17788                     //if (this.toolbars[i].footer) {
17789                     //    tbh += this.toolbars[i].footer.el.getHeight();
17790                     //}
17791                 }
17792               
17793                 
17794                 
17795                 
17796                 
17797                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
17798                 ah -= 5; // knock a few pixes off for look..
17799                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
17800                 var eh = ah;
17801             }
17802         }
17803         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
17804         this.editorcore.onResize(ew,eh);
17805         
17806     },
17807
17808     /**
17809      * Toggles the editor between standard and source edit mode.
17810      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
17811      */
17812     toggleSourceEdit : function(sourceEditMode)
17813     {
17814         this.editorcore.toggleSourceEdit(sourceEditMode);
17815         
17816         if(this.editorcore.sourceEditMode){
17817             Roo.log('editor - showing textarea');
17818             
17819 //            Roo.log('in');
17820 //            Roo.log(this.syncValue());
17821             this.syncValue();
17822             this.inputEl().removeClass(['hide', 'x-hidden']);
17823             this.inputEl().dom.removeAttribute('tabIndex');
17824             this.inputEl().focus();
17825         }else{
17826             Roo.log('editor - hiding textarea');
17827 //            Roo.log('out')
17828 //            Roo.log(this.pushValue()); 
17829             this.pushValue();
17830             
17831             this.inputEl().addClass(['hide', 'x-hidden']);
17832             this.inputEl().dom.setAttribute('tabIndex', -1);
17833             //this.deferFocus();
17834         }
17835          
17836         if(this.resizable){
17837             this.setSize(this.wrap.getSize());
17838         }
17839         
17840         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
17841     },
17842  
17843     // private (for BoxComponent)
17844     adjustSize : Roo.BoxComponent.prototype.adjustSize,
17845
17846     // private (for BoxComponent)
17847     getResizeEl : function(){
17848         return this.wrap;
17849     },
17850
17851     // private (for BoxComponent)
17852     getPositionEl : function(){
17853         return this.wrap;
17854     },
17855
17856     // private
17857     initEvents : function(){
17858         this.originalValue = this.getValue();
17859     },
17860
17861 //    /**
17862 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
17863 //     * @method
17864 //     */
17865 //    markInvalid : Roo.emptyFn,
17866 //    /**
17867 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
17868 //     * @method
17869 //     */
17870 //    clearInvalid : Roo.emptyFn,
17871
17872     setValue : function(v){
17873         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
17874         this.editorcore.pushValue();
17875     },
17876
17877      
17878     // private
17879     deferFocus : function(){
17880         this.focus.defer(10, this);
17881     },
17882
17883     // doc'ed in Field
17884     focus : function(){
17885         this.editorcore.focus();
17886         
17887     },
17888       
17889
17890     // private
17891     onDestroy : function(){
17892         
17893         
17894         
17895         if(this.rendered){
17896             
17897             for (var i =0; i < this.toolbars.length;i++) {
17898                 // fixme - ask toolbars for heights?
17899                 this.toolbars[i].onDestroy();
17900             }
17901             
17902             this.wrap.dom.innerHTML = '';
17903             this.wrap.remove();
17904         }
17905     },
17906
17907     // private
17908     onFirstFocus : function(){
17909         //Roo.log("onFirstFocus");
17910         this.editorcore.onFirstFocus();
17911          for (var i =0; i < this.toolbars.length;i++) {
17912             this.toolbars[i].onFirstFocus();
17913         }
17914         
17915     },
17916     
17917     // private
17918     syncValue : function()
17919     {   
17920         this.editorcore.syncValue();
17921     },
17922     
17923     pushValue : function()
17924     {   
17925         this.editorcore.pushValue();
17926     }
17927      
17928     
17929     // hide stuff that is not compatible
17930     /**
17931      * @event blur
17932      * @hide
17933      */
17934     /**
17935      * @event change
17936      * @hide
17937      */
17938     /**
17939      * @event focus
17940      * @hide
17941      */
17942     /**
17943      * @event specialkey
17944      * @hide
17945      */
17946     /**
17947      * @cfg {String} fieldClass @hide
17948      */
17949     /**
17950      * @cfg {String} focusClass @hide
17951      */
17952     /**
17953      * @cfg {String} autoCreate @hide
17954      */
17955     /**
17956      * @cfg {String} inputType @hide
17957      */
17958     /**
17959      * @cfg {String} invalidClass @hide
17960      */
17961     /**
17962      * @cfg {String} invalidText @hide
17963      */
17964     /**
17965      * @cfg {String} msgFx @hide
17966      */
17967     /**
17968      * @cfg {String} validateOnBlur @hide
17969      */
17970 });
17971  
17972     
17973    
17974    
17975    
17976       
17977 Roo.namespace('Roo.bootstrap.htmleditor');
17978 /**
17979  * @class Roo.bootstrap.HtmlEditorToolbar1
17980  * Basic Toolbar
17981  * 
17982  * Usage:
17983  *
17984  new Roo.bootstrap.HtmlEditor({
17985     ....
17986     toolbars : [
17987         new Roo.bootstrap.HtmlEditorToolbar1({
17988             disable : { fonts: 1 , format: 1, ..., ... , ...],
17989             btns : [ .... ]
17990         })
17991     }
17992      
17993  * 
17994  * @cfg {Object} disable List of elements to disable..
17995  * @cfg {Array} btns List of additional buttons.
17996  * 
17997  * 
17998  * NEEDS Extra CSS? 
17999  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
18000  */
18001  
18002 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
18003 {
18004     
18005     Roo.apply(this, config);
18006     
18007     // default disabled, based on 'good practice'..
18008     this.disable = this.disable || {};
18009     Roo.applyIf(this.disable, {
18010         fontSize : true,
18011         colors : true,
18012         specialElements : true
18013     });
18014     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
18015     
18016     this.editor = config.editor;
18017     this.editorcore = config.editor.editorcore;
18018     
18019     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
18020     
18021     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
18022     // dont call parent... till later.
18023 }
18024 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
18025      
18026     bar : true,
18027     
18028     editor : false,
18029     editorcore : false,
18030     
18031     
18032     formats : [
18033         "p" ,  
18034         "h1","h2","h3","h4","h5","h6", 
18035         "pre", "code", 
18036         "abbr", "acronym", "address", "cite", "samp", "var",
18037         'div','span'
18038     ],
18039     
18040     onRender : function(ct, position)
18041     {
18042        // Roo.log("Call onRender: " + this.xtype);
18043         
18044        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
18045        Roo.log(this.el);
18046        this.el.dom.style.marginBottom = '0';
18047        var _this = this;
18048        var editorcore = this.editorcore;
18049        var editor= this.editor;
18050        
18051        var children = [];
18052        var btn = function(id,cmd , toggle, handler){
18053        
18054             var  event = toggle ? 'toggle' : 'click';
18055        
18056             var a = {
18057                 size : 'sm',
18058                 xtype: 'Button',
18059                 xns: Roo.bootstrap,
18060                 glyphicon : id,
18061                 cmd : id || cmd,
18062                 enableToggle:toggle !== false,
18063                 //html : 'submit'
18064                 pressed : toggle ? false : null,
18065                 listeners : {}
18066             }
18067             a.listeners[toggle ? 'toggle' : 'click'] = function() {
18068                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
18069             }
18070             children.push(a);
18071             return a;
18072        }
18073         
18074         var style = {
18075                 xtype: 'Button',
18076                 size : 'sm',
18077                 xns: Roo.bootstrap,
18078                 glyphicon : 'font',
18079                 //html : 'submit'
18080                 menu : {
18081                     xtype: 'Menu',
18082                     xns: Roo.bootstrap,
18083                     items:  []
18084                 }
18085         };
18086         Roo.each(this.formats, function(f) {
18087             style.menu.items.push({
18088                 xtype :'MenuItem',
18089                 xns: Roo.bootstrap,
18090                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
18091                 tagname : f,
18092                 listeners : {
18093                     click : function()
18094                     {
18095                         editorcore.insertTag(this.tagname);
18096                         editor.focus();
18097                     }
18098                 }
18099                 
18100             });
18101         });
18102          children.push(style);   
18103             
18104             
18105         btn('bold',false,true);
18106         btn('italic',false,true);
18107         btn('align-left', 'justifyleft',true);
18108         btn('align-center', 'justifycenter',true);
18109         btn('align-right' , 'justifyright',true);
18110         btn('link', false, false, function(btn) {
18111             //Roo.log("create link?");
18112             var url = prompt(this.createLinkText, this.defaultLinkValue);
18113             if(url && url != 'http:/'+'/'){
18114                 this.editorcore.relayCmd('createlink', url);
18115             }
18116         }),
18117         btn('list','insertunorderedlist',true);
18118         btn('pencil', false,true, function(btn){
18119                 Roo.log(this);
18120                 
18121                 this.toggleSourceEdit(btn.pressed);
18122         });
18123         /*
18124         var cog = {
18125                 xtype: 'Button',
18126                 size : 'sm',
18127                 xns: Roo.bootstrap,
18128                 glyphicon : 'cog',
18129                 //html : 'submit'
18130                 menu : {
18131                     xtype: 'Menu',
18132                     xns: Roo.bootstrap,
18133                     items:  []
18134                 }
18135         };
18136         
18137         cog.menu.items.push({
18138             xtype :'MenuItem',
18139             xns: Roo.bootstrap,
18140             html : Clean styles,
18141             tagname : f,
18142             listeners : {
18143                 click : function()
18144                 {
18145                     editorcore.insertTag(this.tagname);
18146                     editor.focus();
18147                 }
18148             }
18149             
18150         });
18151        */
18152         
18153          
18154        this.xtype = 'NavSimplebar';
18155         
18156         for(var i=0;i< children.length;i++) {
18157             
18158             this.buttons.add(this.addxtypeChild(children[i]));
18159             
18160         }
18161         
18162         editor.on('editorevent', this.updateToolbar, this);
18163     },
18164     onBtnClick : function(id)
18165     {
18166        this.editorcore.relayCmd(id);
18167        this.editorcore.focus();
18168     },
18169     
18170     /**
18171      * Protected method that will not generally be called directly. It triggers
18172      * a toolbar update by reading the markup state of the current selection in the editor.
18173      */
18174     updateToolbar: function(){
18175
18176         if(!this.editorcore.activated){
18177             this.editor.onFirstFocus(); // is this neeed?
18178             return;
18179         }
18180
18181         var btns = this.buttons; 
18182         var doc = this.editorcore.doc;
18183         btns.get('bold').setActive(doc.queryCommandState('bold'));
18184         btns.get('italic').setActive(doc.queryCommandState('italic'));
18185         //btns.get('underline').setActive(doc.queryCommandState('underline'));
18186         
18187         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
18188         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
18189         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
18190         
18191         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
18192         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
18193          /*
18194         
18195         var ans = this.editorcore.getAllAncestors();
18196         if (this.formatCombo) {
18197             
18198             
18199             var store = this.formatCombo.store;
18200             this.formatCombo.setValue("");
18201             for (var i =0; i < ans.length;i++) {
18202                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
18203                     // select it..
18204                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
18205                     break;
18206                 }
18207             }
18208         }
18209         
18210         
18211         
18212         // hides menus... - so this cant be on a menu...
18213         Roo.bootstrap.MenuMgr.hideAll();
18214         */
18215         Roo.bootstrap.MenuMgr.hideAll();
18216         //this.editorsyncValue();
18217     },
18218     onFirstFocus: function() {
18219         this.buttons.each(function(item){
18220            item.enable();
18221         });
18222     },
18223     toggleSourceEdit : function(sourceEditMode){
18224         
18225           
18226         if(sourceEditMode){
18227             Roo.log("disabling buttons");
18228            this.buttons.each( function(item){
18229                 if(item.cmd != 'pencil'){
18230                     item.disable();
18231                 }
18232             });
18233           
18234         }else{
18235             Roo.log("enabling buttons");
18236             if(this.editorcore.initialized){
18237                 this.buttons.each( function(item){
18238                     item.enable();
18239                 });
18240             }
18241             
18242         }
18243         Roo.log("calling toggole on editor");
18244         // tell the editor that it's been pressed..
18245         this.editor.toggleSourceEdit(sourceEditMode);
18246        
18247     }
18248 });
18249
18250
18251
18252
18253
18254 /**
18255  * @class Roo.bootstrap.Table.AbstractSelectionModel
18256  * @extends Roo.util.Observable
18257  * Abstract base class for grid SelectionModels.  It provides the interface that should be
18258  * implemented by descendant classes.  This class should not be directly instantiated.
18259  * @constructor
18260  */
18261 Roo.bootstrap.Table.AbstractSelectionModel = function(){
18262     this.locked = false;
18263     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
18264 };
18265
18266
18267 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
18268     /** @ignore Called by the grid automatically. Do not call directly. */
18269     init : function(grid){
18270         this.grid = grid;
18271         this.initEvents();
18272     },
18273
18274     /**
18275      * Locks the selections.
18276      */
18277     lock : function(){
18278         this.locked = true;
18279     },
18280
18281     /**
18282      * Unlocks the selections.
18283      */
18284     unlock : function(){
18285         this.locked = false;
18286     },
18287
18288     /**
18289      * Returns true if the selections are locked.
18290      * @return {Boolean}
18291      */
18292     isLocked : function(){
18293         return this.locked;
18294     }
18295 });
18296 /**
18297  * @extends Roo.bootstrap.Table.AbstractSelectionModel
18298  * @class Roo.bootstrap.Table.RowSelectionModel
18299  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
18300  * It supports multiple selections and keyboard selection/navigation. 
18301  * @constructor
18302  * @param {Object} config
18303  */
18304
18305 Roo.bootstrap.Table.RowSelectionModel = function(config){
18306     Roo.apply(this, config);
18307     this.selections = new Roo.util.MixedCollection(false, function(o){
18308         return o.id;
18309     });
18310
18311     this.last = false;
18312     this.lastActive = false;
18313
18314     this.addEvents({
18315         /**
18316              * @event selectionchange
18317              * Fires when the selection changes
18318              * @param {SelectionModel} this
18319              */
18320             "selectionchange" : true,
18321         /**
18322              * @event afterselectionchange
18323              * Fires after the selection changes (eg. by key press or clicking)
18324              * @param {SelectionModel} this
18325              */
18326             "afterselectionchange" : true,
18327         /**
18328              * @event beforerowselect
18329              * Fires when a row is selected being selected, return false to cancel.
18330              * @param {SelectionModel} this
18331              * @param {Number} rowIndex The selected index
18332              * @param {Boolean} keepExisting False if other selections will be cleared
18333              */
18334             "beforerowselect" : true,
18335         /**
18336              * @event rowselect
18337              * Fires when a row is selected.
18338              * @param {SelectionModel} this
18339              * @param {Number} rowIndex The selected index
18340              * @param {Roo.data.Record} r The record
18341              */
18342             "rowselect" : true,
18343         /**
18344              * @event rowdeselect
18345              * Fires when a row is deselected.
18346              * @param {SelectionModel} this
18347              * @param {Number} rowIndex The selected index
18348              */
18349         "rowdeselect" : true
18350     });
18351     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
18352     this.locked = false;
18353 };
18354
18355 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
18356     /**
18357      * @cfg {Boolean} singleSelect
18358      * True to allow selection of only one row at a time (defaults to false)
18359      */
18360     singleSelect : false,
18361
18362     // private
18363     initEvents : function(){
18364
18365         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
18366             this.grid.on("mousedown", this.handleMouseDown, this);
18367         }else{ // allow click to work like normal
18368             this.grid.on("rowclick", this.handleDragableRowClick, this);
18369         }
18370
18371         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
18372             "up" : function(e){
18373                 if(!e.shiftKey){
18374                     this.selectPrevious(e.shiftKey);
18375                 }else if(this.last !== false && this.lastActive !== false){
18376                     var last = this.last;
18377                     this.selectRange(this.last,  this.lastActive-1);
18378                     this.grid.getView().focusRow(this.lastActive);
18379                     if(last !== false){
18380                         this.last = last;
18381                     }
18382                 }else{
18383                     this.selectFirstRow();
18384                 }
18385                 this.fireEvent("afterselectionchange", this);
18386             },
18387             "down" : function(e){
18388                 if(!e.shiftKey){
18389                     this.selectNext(e.shiftKey);
18390                 }else if(this.last !== false && this.lastActive !== false){
18391                     var last = this.last;
18392                     this.selectRange(this.last,  this.lastActive+1);
18393                     this.grid.getView().focusRow(this.lastActive);
18394                     if(last !== false){
18395                         this.last = last;
18396                     }
18397                 }else{
18398                     this.selectFirstRow();
18399                 }
18400                 this.fireEvent("afterselectionchange", this);
18401             },
18402             scope: this
18403         });
18404
18405         var view = this.grid.view;
18406         view.on("refresh", this.onRefresh, this);
18407         view.on("rowupdated", this.onRowUpdated, this);
18408         view.on("rowremoved", this.onRemove, this);
18409     },
18410
18411     // private
18412     onRefresh : function(){
18413         var ds = this.grid.dataSource, i, v = this.grid.view;
18414         var s = this.selections;
18415         s.each(function(r){
18416             if((i = ds.indexOfId(r.id)) != -1){
18417                 v.onRowSelect(i);
18418             }else{
18419                 s.remove(r);
18420             }
18421         });
18422     },
18423
18424     // private
18425     onRemove : function(v, index, r){
18426         this.selections.remove(r);
18427     },
18428
18429     // private
18430     onRowUpdated : function(v, index, r){
18431         if(this.isSelected(r)){
18432             v.onRowSelect(index);
18433         }
18434     },
18435
18436     /**
18437      * Select records.
18438      * @param {Array} records The records to select
18439      * @param {Boolean} keepExisting (optional) True to keep existing selections
18440      */
18441     selectRecords : function(records, keepExisting){
18442         if(!keepExisting){
18443             this.clearSelections();
18444         }
18445         var ds = this.grid.dataSource;
18446         for(var i = 0, len = records.length; i < len; i++){
18447             this.selectRow(ds.indexOf(records[i]), true);
18448         }
18449     },
18450
18451     /**
18452      * Gets the number of selected rows.
18453      * @return {Number}
18454      */
18455     getCount : function(){
18456         return this.selections.length;
18457     },
18458
18459     /**
18460      * Selects the first row in the grid.
18461      */
18462     selectFirstRow : function(){
18463         this.selectRow(0);
18464     },
18465
18466     /**
18467      * Select the last row.
18468      * @param {Boolean} keepExisting (optional) True to keep existing selections
18469      */
18470     selectLastRow : function(keepExisting){
18471         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
18472     },
18473
18474     /**
18475      * Selects the row immediately following the last selected row.
18476      * @param {Boolean} keepExisting (optional) True to keep existing selections
18477      */
18478     selectNext : function(keepExisting){
18479         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
18480             this.selectRow(this.last+1, keepExisting);
18481             this.grid.getView().focusRow(this.last);
18482         }
18483     },
18484
18485     /**
18486      * Selects the row that precedes the last selected row.
18487      * @param {Boolean} keepExisting (optional) True to keep existing selections
18488      */
18489     selectPrevious : function(keepExisting){
18490         if(this.last){
18491             this.selectRow(this.last-1, keepExisting);
18492             this.grid.getView().focusRow(this.last);
18493         }
18494     },
18495
18496     /**
18497      * Returns the selected records
18498      * @return {Array} Array of selected records
18499      */
18500     getSelections : function(){
18501         return [].concat(this.selections.items);
18502     },
18503
18504     /**
18505      * Returns the first selected record.
18506      * @return {Record}
18507      */
18508     getSelected : function(){
18509         return this.selections.itemAt(0);
18510     },
18511
18512
18513     /**
18514      * Clears all selections.
18515      */
18516     clearSelections : function(fast){
18517         if(this.locked) return;
18518         if(fast !== true){
18519             var ds = this.grid.dataSource;
18520             var s = this.selections;
18521             s.each(function(r){
18522                 this.deselectRow(ds.indexOfId(r.id));
18523             }, this);
18524             s.clear();
18525         }else{
18526             this.selections.clear();
18527         }
18528         this.last = false;
18529     },
18530
18531
18532     /**
18533      * Selects all rows.
18534      */
18535     selectAll : function(){
18536         if(this.locked) return;
18537         this.selections.clear();
18538         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
18539             this.selectRow(i, true);
18540         }
18541     },
18542
18543     /**
18544      * Returns True if there is a selection.
18545      * @return {Boolean}
18546      */
18547     hasSelection : function(){
18548         return this.selections.length > 0;
18549     },
18550
18551     /**
18552      * Returns True if the specified row is selected.
18553      * @param {Number/Record} record The record or index of the record to check
18554      * @return {Boolean}
18555      */
18556     isSelected : function(index){
18557         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
18558         return (r && this.selections.key(r.id) ? true : false);
18559     },
18560
18561     /**
18562      * Returns True if the specified record id is selected.
18563      * @param {String} id The id of record to check
18564      * @return {Boolean}
18565      */
18566     isIdSelected : function(id){
18567         return (this.selections.key(id) ? true : false);
18568     },
18569
18570     // private
18571     handleMouseDown : function(e, t){
18572         var view = this.grid.getView(), rowIndex;
18573         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
18574             return;
18575         };
18576         if(e.shiftKey && this.last !== false){
18577             var last = this.last;
18578             this.selectRange(last, rowIndex, e.ctrlKey);
18579             this.last = last; // reset the last
18580             view.focusRow(rowIndex);
18581         }else{
18582             var isSelected = this.isSelected(rowIndex);
18583             if(e.button !== 0 && isSelected){
18584                 view.focusRow(rowIndex);
18585             }else if(e.ctrlKey && isSelected){
18586                 this.deselectRow(rowIndex);
18587             }else if(!isSelected){
18588                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
18589                 view.focusRow(rowIndex);
18590             }
18591         }
18592         this.fireEvent("afterselectionchange", this);
18593     },
18594     // private
18595     handleDragableRowClick :  function(grid, rowIndex, e) 
18596     {
18597         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
18598             this.selectRow(rowIndex, false);
18599             grid.view.focusRow(rowIndex);
18600              this.fireEvent("afterselectionchange", this);
18601         }
18602     },
18603     
18604     /**
18605      * Selects multiple rows.
18606      * @param {Array} rows Array of the indexes of the row to select
18607      * @param {Boolean} keepExisting (optional) True to keep existing selections
18608      */
18609     selectRows : function(rows, keepExisting){
18610         if(!keepExisting){
18611             this.clearSelections();
18612         }
18613         for(var i = 0, len = rows.length; i < len; i++){
18614             this.selectRow(rows[i], true);
18615         }
18616     },
18617
18618     /**
18619      * Selects a range of rows. All rows in between startRow and endRow are also selected.
18620      * @param {Number} startRow The index of the first row in the range
18621      * @param {Number} endRow The index of the last row in the range
18622      * @param {Boolean} keepExisting (optional) True to retain existing selections
18623      */
18624     selectRange : function(startRow, endRow, keepExisting){
18625         if(this.locked) return;
18626         if(!keepExisting){
18627             this.clearSelections();
18628         }
18629         if(startRow <= endRow){
18630             for(var i = startRow; i <= endRow; i++){
18631                 this.selectRow(i, true);
18632             }
18633         }else{
18634             for(var i = startRow; i >= endRow; i--){
18635                 this.selectRow(i, true);
18636             }
18637         }
18638     },
18639
18640     /**
18641      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
18642      * @param {Number} startRow The index of the first row in the range
18643      * @param {Number} endRow The index of the last row in the range
18644      */
18645     deselectRange : function(startRow, endRow, preventViewNotify){
18646         if(this.locked) return;
18647         for(var i = startRow; i <= endRow; i++){
18648             this.deselectRow(i, preventViewNotify);
18649         }
18650     },
18651
18652     /**
18653      * Selects a row.
18654      * @param {Number} row The index of the row to select
18655      * @param {Boolean} keepExisting (optional) True to keep existing selections
18656      */
18657     selectRow : function(index, keepExisting, preventViewNotify){
18658         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
18659         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
18660             if(!keepExisting || this.singleSelect){
18661                 this.clearSelections();
18662             }
18663             var r = this.grid.dataSource.getAt(index);
18664             this.selections.add(r);
18665             this.last = this.lastActive = index;
18666             if(!preventViewNotify){
18667                 this.grid.getView().onRowSelect(index);
18668             }
18669             this.fireEvent("rowselect", this, index, r);
18670             this.fireEvent("selectionchange", this);
18671         }
18672     },
18673
18674     /**
18675      * Deselects a row.
18676      * @param {Number} row The index of the row to deselect
18677      */
18678     deselectRow : function(index, preventViewNotify){
18679         if(this.locked) return;
18680         if(this.last == index){
18681             this.last = false;
18682         }
18683         if(this.lastActive == index){
18684             this.lastActive = false;
18685         }
18686         var r = this.grid.dataSource.getAt(index);
18687         this.selections.remove(r);
18688         if(!preventViewNotify){
18689             this.grid.getView().onRowDeselect(index);
18690         }
18691         this.fireEvent("rowdeselect", this, index);
18692         this.fireEvent("selectionchange", this);
18693     },
18694
18695     // private
18696     restoreLast : function(){
18697         if(this._last){
18698             this.last = this._last;
18699         }
18700     },
18701
18702     // private
18703     acceptsNav : function(row, col, cm){
18704         return !cm.isHidden(col) && cm.isCellEditable(col, row);
18705     },
18706
18707     // private
18708     onEditorKey : function(field, e){
18709         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
18710         if(k == e.TAB){
18711             e.stopEvent();
18712             ed.completeEdit();
18713             if(e.shiftKey){
18714                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
18715             }else{
18716                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
18717             }
18718         }else if(k == e.ENTER && !e.ctrlKey){
18719             e.stopEvent();
18720             ed.completeEdit();
18721             if(e.shiftKey){
18722                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
18723             }else{
18724                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
18725             }
18726         }else if(k == e.ESC){
18727             ed.cancelEdit();
18728         }
18729         if(newCell){
18730             g.startEditing(newCell[0], newCell[1]);
18731         }
18732     }
18733 });/*
18734  * Based on:
18735  * Ext JS Library 1.1.1
18736  * Copyright(c) 2006-2007, Ext JS, LLC.
18737  *
18738  * Originally Released Under LGPL - original licence link has changed is not relivant.
18739  *
18740  * Fork - LGPL
18741  * <script type="text/javascript">
18742  */
18743  
18744 /**
18745  * @class Roo.bootstrap.PagingToolbar
18746  * @extends Roo.Row
18747  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
18748  * @constructor
18749  * Create a new PagingToolbar
18750  * @param {Object} config The config object
18751  */
18752 Roo.bootstrap.PagingToolbar = function(config)
18753 {
18754     // old args format still supported... - xtype is prefered..
18755         // created from xtype...
18756     var ds = config.dataSource;
18757     this.toolbarItems = [];
18758     if (config.items) {
18759         this.toolbarItems = config.items;
18760 //        config.items = [];
18761     }
18762     
18763     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
18764     this.ds = ds;
18765     this.cursor = 0;
18766     if (ds) { 
18767         this.bind(ds);
18768     }
18769     
18770     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
18771     
18772 };
18773
18774 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
18775     /**
18776      * @cfg {Roo.data.Store} dataSource
18777      * The underlying data store providing the paged data
18778      */
18779     /**
18780      * @cfg {String/HTMLElement/Element} container
18781      * container The id or element that will contain the toolbar
18782      */
18783     /**
18784      * @cfg {Boolean} displayInfo
18785      * True to display the displayMsg (defaults to false)
18786      */
18787     /**
18788      * @cfg {Number} pageSize
18789      * The number of records to display per page (defaults to 20)
18790      */
18791     pageSize: 20,
18792     /**
18793      * @cfg {String} displayMsg
18794      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
18795      */
18796     displayMsg : 'Displaying {0} - {1} of {2}',
18797     /**
18798      * @cfg {String} emptyMsg
18799      * The message to display when no records are found (defaults to "No data to display")
18800      */
18801     emptyMsg : 'No data to display',
18802     /**
18803      * Customizable piece of the default paging text (defaults to "Page")
18804      * @type String
18805      */
18806     beforePageText : "Page",
18807     /**
18808      * Customizable piece of the default paging text (defaults to "of %0")
18809      * @type String
18810      */
18811     afterPageText : "of {0}",
18812     /**
18813      * Customizable piece of the default paging text (defaults to "First Page")
18814      * @type String
18815      */
18816     firstText : "First Page",
18817     /**
18818      * Customizable piece of the default paging text (defaults to "Previous Page")
18819      * @type String
18820      */
18821     prevText : "Previous Page",
18822     /**
18823      * Customizable piece of the default paging text (defaults to "Next Page")
18824      * @type String
18825      */
18826     nextText : "Next Page",
18827     /**
18828      * Customizable piece of the default paging text (defaults to "Last Page")
18829      * @type String
18830      */
18831     lastText : "Last Page",
18832     /**
18833      * Customizable piece of the default paging text (defaults to "Refresh")
18834      * @type String
18835      */
18836     refreshText : "Refresh",
18837
18838     buttons : false,
18839     // private
18840     onRender : function(ct, position) 
18841     {
18842         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
18843         this.navgroup.parentId = this.id;
18844         this.navgroup.onRender(this.el, null);
18845         // add the buttons to the navgroup
18846         
18847         if(this.displayInfo){
18848             Roo.log(this.el.select('ul.navbar-nav',true).first());
18849             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
18850             this.displayEl = this.el.select('.x-paging-info', true).first();
18851 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
18852 //            this.displayEl = navel.el.select('span',true).first();
18853         }
18854         
18855         var _this = this;
18856         
18857         if(this.buttons){
18858             Roo.each(_this.buttons, function(e){
18859                Roo.factory(e).onRender(_this.el, null);
18860             });
18861         }
18862             
18863         Roo.each(_this.toolbarItems, function(e) {
18864             _this.navgroup.addItem(e);
18865         });
18866         
18867         this.first = this.navgroup.addItem({
18868             tooltip: this.firstText,
18869             cls: "prev",
18870             icon : 'fa fa-backward',
18871             disabled: true,
18872             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
18873         });
18874         
18875         this.prev =  this.navgroup.addItem({
18876             tooltip: this.prevText,
18877             cls: "prev",
18878             icon : 'fa fa-step-backward',
18879             disabled: true,
18880             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
18881         });
18882     //this.addSeparator();
18883         
18884         
18885         var field = this.navgroup.addItem( {
18886             tagtype : 'span',
18887             cls : 'x-paging-position',
18888             
18889             html : this.beforePageText  +
18890                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
18891                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
18892          } ); //?? escaped?
18893         
18894         this.field = field.el.select('input', true).first();
18895         this.field.on("keydown", this.onPagingKeydown, this);
18896         this.field.on("focus", function(){this.dom.select();});
18897     
18898     
18899         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
18900         //this.field.setHeight(18);
18901         //this.addSeparator();
18902         this.next = this.navgroup.addItem({
18903             tooltip: this.nextText,
18904             cls: "next",
18905             html : ' <i class="fa fa-step-forward">',
18906             disabled: true,
18907             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
18908         });
18909         this.last = this.navgroup.addItem({
18910             tooltip: this.lastText,
18911             icon : 'fa fa-forward',
18912             cls: "next",
18913             disabled: true,
18914             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
18915         });
18916     //this.addSeparator();
18917         this.loading = this.navgroup.addItem({
18918             tooltip: this.refreshText,
18919             icon: 'fa fa-refresh',
18920             
18921             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
18922         });
18923
18924     },
18925
18926     // private
18927     updateInfo : function(){
18928         if(this.displayEl){
18929             var count = this.ds.getCount();
18930             var msg = count == 0 ?
18931                 this.emptyMsg :
18932                 String.format(
18933                     this.displayMsg,
18934                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
18935                 );
18936             this.displayEl.update(msg);
18937         }
18938     },
18939
18940     // private
18941     onLoad : function(ds, r, o){
18942        this.cursor = o.params ? o.params.start : 0;
18943        var d = this.getPageData(),
18944             ap = d.activePage,
18945             ps = d.pages;
18946         
18947        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
18948        this.field.dom.value = ap;
18949        this.first.setDisabled(ap == 1);
18950        this.prev.setDisabled(ap == 1);
18951        this.next.setDisabled(ap == ps);
18952        this.last.setDisabled(ap == ps);
18953        this.loading.enable();
18954        this.updateInfo();
18955     },
18956
18957     // private
18958     getPageData : function(){
18959         var total = this.ds.getTotalCount();
18960         return {
18961             total : total,
18962             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
18963             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
18964         };
18965     },
18966
18967     // private
18968     onLoadError : function(){
18969         this.loading.enable();
18970     },
18971
18972     // private
18973     onPagingKeydown : function(e){
18974         var k = e.getKey();
18975         var d = this.getPageData();
18976         if(k == e.RETURN){
18977             var v = this.field.dom.value, pageNum;
18978             if(!v || isNaN(pageNum = parseInt(v, 10))){
18979                 this.field.dom.value = d.activePage;
18980                 return;
18981             }
18982             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
18983             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
18984             e.stopEvent();
18985         }
18986         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))
18987         {
18988           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
18989           this.field.dom.value = pageNum;
18990           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
18991           e.stopEvent();
18992         }
18993         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
18994         {
18995           var v = this.field.dom.value, pageNum; 
18996           var increment = (e.shiftKey) ? 10 : 1;
18997           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
18998             increment *= -1;
18999           if(!v || isNaN(pageNum = parseInt(v, 10))) {
19000             this.field.dom.value = d.activePage;
19001             return;
19002           }
19003           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
19004           {
19005             this.field.dom.value = parseInt(v, 10) + increment;
19006             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
19007             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
19008           }
19009           e.stopEvent();
19010         }
19011     },
19012
19013     // private
19014     beforeLoad : function(){
19015         if(this.loading){
19016             this.loading.disable();
19017         }
19018     },
19019
19020     // private
19021     onClick : function(which){
19022         var ds = this.ds;
19023         if (!ds) {
19024             return;
19025         }
19026         switch(which){
19027             case "first":
19028                 ds.load({params:{start: 0, limit: this.pageSize}});
19029             break;
19030             case "prev":
19031                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
19032             break;
19033             case "next":
19034                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
19035             break;
19036             case "last":
19037                 var total = ds.getTotalCount();
19038                 var extra = total % this.pageSize;
19039                 var lastStart = extra ? (total - extra) : total-this.pageSize;
19040                 ds.load({params:{start: lastStart, limit: this.pageSize}});
19041             break;
19042             case "refresh":
19043                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
19044             break;
19045         }
19046     },
19047
19048     /**
19049      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
19050      * @param {Roo.data.Store} store The data store to unbind
19051      */
19052     unbind : function(ds){
19053         ds.un("beforeload", this.beforeLoad, this);
19054         ds.un("load", this.onLoad, this);
19055         ds.un("loadexception", this.onLoadError, this);
19056         ds.un("remove", this.updateInfo, this);
19057         ds.un("add", this.updateInfo, this);
19058         this.ds = undefined;
19059     },
19060
19061     /**
19062      * Binds the paging toolbar to the specified {@link Roo.data.Store}
19063      * @param {Roo.data.Store} store The data store to bind
19064      */
19065     bind : function(ds){
19066         ds.on("beforeload", this.beforeLoad, this);
19067         ds.on("load", this.onLoad, this);
19068         ds.on("loadexception", this.onLoadError, this);
19069         ds.on("remove", this.updateInfo, this);
19070         ds.on("add", this.updateInfo, this);
19071         this.ds = ds;
19072     }
19073 });/*
19074  * - LGPL
19075  *
19076  * element
19077  * 
19078  */
19079
19080 /**
19081  * @class Roo.bootstrap.MessageBar
19082  * @extends Roo.bootstrap.Component
19083  * Bootstrap MessageBar class
19084  * @cfg {String} html contents of the MessageBar
19085  * @cfg {String} weight (info | success | warning | danger) default info
19086  * @cfg {String} beforeClass insert the bar before the given class
19087  * @cfg {Boolean} closable (true | false) default false
19088  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
19089  * 
19090  * @constructor
19091  * Create a new Element
19092  * @param {Object} config The config object
19093  */
19094
19095 Roo.bootstrap.MessageBar = function(config){
19096     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
19097 };
19098
19099 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
19100     
19101     html: '',
19102     weight: 'info',
19103     closable: false,
19104     fixed: false,
19105     beforeClass: 'bootstrap-sticky-wrap',
19106     
19107     getAutoCreate : function(){
19108         
19109         var cfg = {
19110             tag: 'div',
19111             cls: 'alert alert-dismissable alert-' + this.weight,
19112             cn: [
19113                 {
19114                     tag: 'span',
19115                     cls: 'message',
19116                     html: this.html || ''
19117                 }
19118             ]
19119         }
19120         
19121         if(this.fixed){
19122             cfg.cls += ' alert-messages-fixed';
19123         }
19124         
19125         if(this.closable){
19126             cfg.cn.push({
19127                 tag: 'button',
19128                 cls: 'close',
19129                 html: 'x'
19130             });
19131         }
19132         
19133         return cfg;
19134     },
19135     
19136     onRender : function(ct, position)
19137     {
19138         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19139         
19140         if(!this.el){
19141             var cfg = Roo.apply({},  this.getAutoCreate());
19142             cfg.id = Roo.id();
19143             
19144             if (this.cls) {
19145                 cfg.cls += ' ' + this.cls;
19146             }
19147             if (this.style) {
19148                 cfg.style = this.style;
19149             }
19150             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
19151             
19152             this.el.setVisibilityMode(Roo.Element.DISPLAY);
19153         }
19154         
19155         this.el.select('>button.close').on('click', this.hide, this);
19156         
19157     },
19158     
19159     show : function()
19160     {
19161         if (!this.rendered) {
19162             this.render();
19163         }
19164         
19165         this.el.show();
19166         
19167         this.fireEvent('show', this);
19168         
19169     },
19170     
19171     hide : function()
19172     {
19173         if (!this.rendered) {
19174             this.render();
19175         }
19176         
19177         this.el.hide();
19178         
19179         this.fireEvent('hide', this);
19180     },
19181     
19182     update : function()
19183     {
19184 //        var e = this.el.dom.firstChild;
19185 //        
19186 //        if(this.closable){
19187 //            e = e.nextSibling;
19188 //        }
19189 //        
19190 //        e.data = this.html || '';
19191
19192         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
19193     }
19194    
19195 });
19196
19197  
19198
19199      /*
19200  * - LGPL
19201  *
19202  * Graph
19203  * 
19204  */
19205
19206
19207 /**
19208  * @class Roo.bootstrap.Graph
19209  * @extends Roo.bootstrap.Component
19210  * Bootstrap Graph class
19211 > Prameters
19212  -sm {number} sm 4
19213  -md {number} md 5
19214  @cfg {String} graphtype  bar | vbar | pie
19215  @cfg {number} g_x coodinator | centre x (pie)
19216  @cfg {number} g_y coodinator | centre y (pie)
19217  @cfg {number} g_r radius (pie)
19218  @cfg {number} g_height height of the chart (respected by all elements in the set)
19219  @cfg {number} g_width width of the chart (respected by all elements in the set)
19220  @cfg {Object} title The title of the chart
19221     
19222  -{Array}  values
19223  -opts (object) options for the chart 
19224      o {
19225      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
19226      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
19227      o vgutter (number)
19228      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.
19229      o stacked (boolean) whether or not to tread values as in a stacked bar chart
19230      o to
19231      o stretch (boolean)
19232      o }
19233  -opts (object) options for the pie
19234      o{
19235      o cut
19236      o startAngle (number)
19237      o endAngle (number)
19238      } 
19239  *
19240  * @constructor
19241  * Create a new Input
19242  * @param {Object} config The config object
19243  */
19244
19245 Roo.bootstrap.Graph = function(config){
19246     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
19247     
19248     this.addEvents({
19249         // img events
19250         /**
19251          * @event click
19252          * The img click event for the img.
19253          * @param {Roo.EventObject} e
19254          */
19255         "click" : true
19256     });
19257 };
19258
19259 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
19260     
19261     sm: 4,
19262     md: 5,
19263     graphtype: 'bar',
19264     g_height: 250,
19265     g_width: 400,
19266     g_x: 50,
19267     g_y: 50,
19268     g_r: 30,
19269     opts:{
19270         //g_colors: this.colors,
19271         g_type: 'soft',
19272         g_gutter: '20%'
19273
19274     },
19275     title : false,
19276
19277     getAutoCreate : function(){
19278         
19279         var cfg = {
19280             tag: 'div',
19281             html : null
19282         }
19283         
19284         
19285         return  cfg;
19286     },
19287
19288     onRender : function(ct,position){
19289         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
19290         this.raphael = Raphael(this.el.dom);
19291         
19292                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19293                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19294                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19295                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
19296                 /*
19297                 r.text(160, 10, "Single Series Chart").attr(txtattr);
19298                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
19299                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
19300                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
19301                 
19302                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
19303                 r.barchart(330, 10, 300, 220, data1);
19304                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
19305                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
19306                 */
19307                 
19308                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19309                 // r.barchart(30, 30, 560, 250,  xdata, {
19310                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
19311                 //     axis : "0 0 1 1",
19312                 //     axisxlabels :  xdata
19313                 //     //yvalues : cols,
19314                    
19315                 // });
19316 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19317 //        
19318 //        this.load(null,xdata,{
19319 //                axis : "0 0 1 1",
19320 //                axisxlabels :  xdata
19321 //                });
19322
19323     },
19324
19325     load : function(graphtype,xdata,opts){
19326         this.raphael.clear();
19327         if(!graphtype) {
19328             graphtype = this.graphtype;
19329         }
19330         if(!opts){
19331             opts = this.opts;
19332         }
19333         var r = this.raphael,
19334             fin = function () {
19335                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
19336             },
19337             fout = function () {
19338                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
19339             },
19340             pfin = function() {
19341                 this.sector.stop();
19342                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
19343
19344                 if (this.label) {
19345                     this.label[0].stop();
19346                     this.label[0].attr({ r: 7.5 });
19347                     this.label[1].attr({ "font-weight": 800 });
19348                 }
19349             },
19350             pfout = function() {
19351                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
19352
19353                 if (this.label) {
19354                     this.label[0].animate({ r: 5 }, 500, "bounce");
19355                     this.label[1].attr({ "font-weight": 400 });
19356                 }
19357             };
19358
19359         switch(graphtype){
19360             case 'bar':
19361                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19362                 break;
19363             case 'hbar':
19364                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19365                 break;
19366             case 'pie':
19367 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
19368 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
19369 //            
19370                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
19371                 
19372                 break;
19373
19374         }
19375         
19376         if(this.title){
19377             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
19378         }
19379         
19380     },
19381     
19382     setTitle: function(o)
19383     {
19384         this.title = o;
19385     },
19386     
19387     initEvents: function() {
19388         
19389         if(!this.href){
19390             this.el.on('click', this.onClick, this);
19391         }
19392     },
19393     
19394     onClick : function(e)
19395     {
19396         Roo.log('img onclick');
19397         this.fireEvent('click', this, e);
19398     }
19399    
19400 });
19401
19402  
19403 /*
19404  * - LGPL
19405  *
19406  * numberBox
19407  * 
19408  */
19409 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19410
19411 /**
19412  * @class Roo.bootstrap.dash.NumberBox
19413  * @extends Roo.bootstrap.Component
19414  * Bootstrap NumberBox class
19415  * @cfg {String} bgcolor Box background color, such as (aqua | green | yellow | red etc..) Default aqua
19416  * @cfg {String} headline Box headline
19417  * @cfg {String} content Box content
19418  * @cfg {String} icon Box icon
19419  * @cfg {String} footer Footer text
19420  * @cfg {String} fhref Footer href
19421  * 
19422  * @constructor
19423  * Create a new NumberBox
19424  * @param {Object} config The config object
19425  */
19426
19427
19428 Roo.bootstrap.dash.NumberBox = function(config){
19429     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
19430     
19431 };
19432
19433 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
19434     
19435     bgcolor : 'aqua',
19436     headline : '',
19437     content : '',
19438     icon : '',
19439     footer : '',
19440     fhref : '',
19441     ficon : '',
19442     
19443     getAutoCreate : function(){
19444         
19445         var cfg = {
19446             tag : 'div',
19447             cls : 'small-box bg-' + this.bgcolor,
19448             cn : [
19449                 {
19450                     tag : 'div',
19451                     cls : 'inner',
19452                     cn :[
19453                         {
19454                             tag : 'h3',
19455                             cls : 'roo-headline',
19456                             html : this.headline
19457                         },
19458                         {
19459                             tag : 'p',
19460                             cls : 'roo-content',
19461                             html : this.content
19462                         }
19463                     ]
19464                 }
19465             ]
19466         }
19467         
19468         if(this.icon){
19469             cfg.cn.push({
19470                 tag : 'div',
19471                 cls : 'icon',
19472                 cn :[
19473                     {
19474                         tag : 'i',
19475                         cls : 'ion ' + this.icon
19476                     }
19477                 ]
19478             });
19479         }
19480         
19481         if(this.footer){
19482             var footer = {
19483                 tag : 'a',
19484                 cls : 'small-box-footer',
19485                 href : this.fhref || '#',
19486                 html : this.footer
19487             };
19488             
19489             cfg.cn.push(footer);
19490             
19491         }
19492         
19493         return  cfg;
19494     },
19495
19496     onRender : function(ct,position){
19497         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
19498
19499
19500        
19501                 
19502     },
19503
19504     setHeadline: function (value)
19505     {
19506         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
19507     },
19508     
19509     setFooter: function (value, href)
19510     {
19511         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
19512         
19513         if(href){
19514             this.el.select('a.small-box-footer',true).first().attr('href', href);
19515         }
19516         
19517     },
19518
19519     setContent: function (value)
19520     {
19521         this.el.select('.roo-content',true).first().dom.innerHTML = value;
19522     },
19523
19524     initEvents: function() 
19525     {   
19526         
19527     }
19528     
19529 });
19530
19531  
19532 /*
19533  * - LGPL
19534  *
19535  * TabBox
19536  * 
19537  */
19538 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19539
19540 /**
19541  * @class Roo.bootstrap.dash.TabBox
19542  * @extends Roo.bootstrap.Component
19543  * Bootstrap TabBox class
19544  * @cfg {String} title Title of the TabBox
19545  * @cfg {String} icon Icon of the TabBox
19546  * @cfg {Boolean} showtabs (true|false) show the tabs default true
19547  * 
19548  * @constructor
19549  * Create a new TabBox
19550  * @param {Object} config The config object
19551  */
19552
19553
19554 Roo.bootstrap.dash.TabBox = function(config){
19555     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
19556     this.addEvents({
19557         // raw events
19558         /**
19559          * @event addpane
19560          * When a pane is added
19561          * @param {Roo.bootstrap.dash.TabPane} pane
19562          */
19563         "addpane" : true
19564          
19565     });
19566 };
19567
19568 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
19569
19570     title : '',
19571     icon : false,
19572     showtabs : true,
19573     
19574     getChildContainer : function()
19575     {
19576         return this.el.select('.tab-content', true).first();
19577     },
19578     
19579     getAutoCreate : function(){
19580         
19581         var header = {
19582             tag: 'li',
19583             cls: 'pull-left header',
19584             html: this.title,
19585             cn : []
19586         };
19587         
19588         if(this.icon){
19589             header.cn.push({
19590                 tag: 'i',
19591                 cls: 'fa ' + this.icon
19592             });
19593         }
19594         
19595         
19596         var cfg = {
19597             tag: 'div',
19598             cls: 'nav-tabs-custom',
19599             cn: [
19600                 {
19601                     tag: 'ul',
19602                     cls: 'nav nav-tabs pull-right',
19603                     cn: [
19604                         header
19605                     ]
19606                 },
19607                 {
19608                     tag: 'div',
19609                     cls: 'tab-content no-padding',
19610                     cn: []
19611                 }
19612             ]
19613         }
19614
19615         return  cfg;
19616     },
19617     initEvents : function()
19618     {
19619         //Roo.log('add add pane handler');
19620         this.on('addpane', this.onAddPane, this);
19621     },
19622      /**
19623      * Updates the box title
19624      * @param {String} html to set the title to.
19625      */
19626     setTitle : function(value)
19627     {
19628         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
19629     },
19630     onAddPane : function(pane)
19631     {
19632         //Roo.log('addpane');
19633         //Roo.log(pane);
19634         // tabs are rendere left to right..
19635         if(!this.showtabs){
19636             return;
19637         }
19638         
19639         var ctr = this.el.select('.nav-tabs', true).first();
19640          
19641          
19642         var existing = ctr.select('.nav-tab',true);
19643         var qty = existing.getCount();;
19644         
19645         
19646         var tab = ctr.createChild({
19647             tag : 'li',
19648             cls : 'nav-tab' + (qty ? '' : ' active'),
19649             cn : [
19650                 {
19651                     tag : 'a',
19652                     href:'#',
19653                     html : pane.title
19654                 }
19655             ]
19656         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
19657         pane.tab = tab;
19658         
19659         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
19660         if (!qty) {
19661             pane.el.addClass('active');
19662         }
19663         
19664                 
19665     },
19666     onTabClick : function(ev,un,ob,pane)
19667     {
19668         //Roo.log('tab - prev default');
19669         ev.preventDefault();
19670         
19671         
19672         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
19673         pane.tab.addClass('active');
19674         //Roo.log(pane.title);
19675         this.getChildContainer().select('.tab-pane',true).removeClass('active');
19676         // technically we should have a deactivate event.. but maybe add later.
19677         // and it should not de-activate the selected tab...
19678         
19679         pane.el.addClass('active');
19680         pane.fireEvent('activate');
19681         
19682         
19683     }
19684     
19685     
19686 });
19687
19688  
19689 /*
19690  * - LGPL
19691  *
19692  * Tab pane
19693  * 
19694  */
19695 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19696 /**
19697  * @class Roo.bootstrap.TabPane
19698  * @extends Roo.bootstrap.Component
19699  * Bootstrap TabPane class
19700  * @cfg {Boolean} active (false | true) Default false
19701  * @cfg {String} title title of panel
19702
19703  * 
19704  * @constructor
19705  * Create a new TabPane
19706  * @param {Object} config The config object
19707  */
19708
19709 Roo.bootstrap.dash.TabPane = function(config){
19710     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
19711     
19712 };
19713
19714 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
19715     
19716     active : false,
19717     title : '',
19718     
19719     // the tabBox that this is attached to.
19720     tab : false,
19721      
19722     getAutoCreate : function() 
19723     {
19724         var cfg = {
19725             tag: 'div',
19726             cls: 'tab-pane'
19727         }
19728         
19729         if(this.active){
19730             cfg.cls += ' active';
19731         }
19732         
19733         return cfg;
19734     },
19735     initEvents  : function()
19736     {
19737         //Roo.log('trigger add pane handler');
19738         this.parent().fireEvent('addpane', this)
19739     },
19740     
19741      /**
19742      * Updates the tab title 
19743      * @param {String} html to set the title to.
19744      */
19745     setTitle: function(str)
19746     {
19747         if (!this.tab) {
19748             return;
19749         }
19750         this.title = str;
19751         this.tab.select('a'.true).first().dom.innerHTML = str;
19752         
19753     }
19754     
19755     
19756     
19757 });
19758
19759  
19760
19761
19762  /*
19763  * - LGPL
19764  *
19765  * menu
19766  * 
19767  */
19768 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
19769
19770 /**
19771  * @class Roo.bootstrap.menu.Menu
19772  * @extends Roo.bootstrap.Component
19773  * Bootstrap Menu class - container for Menu
19774  * @cfg {String} html Text of the menu
19775  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
19776  * @cfg {String} icon Font awesome icon
19777  * @cfg {String} pos Menu align to (top | bottom) default bottom
19778  * 
19779  * 
19780  * @constructor
19781  * Create a new Menu
19782  * @param {Object} config The config object
19783  */
19784
19785
19786 Roo.bootstrap.menu.Menu = function(config){
19787     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
19788     
19789     this.addEvents({
19790         /**
19791          * @event beforeshow
19792          * Fires before this menu is displayed
19793          * @param {Roo.bootstrap.menu.Menu} this
19794          */
19795         beforeshow : true,
19796         /**
19797          * @event beforehide
19798          * Fires before this menu is hidden
19799          * @param {Roo.bootstrap.menu.Menu} this
19800          */
19801         beforehide : true,
19802         /**
19803          * @event show
19804          * Fires after this menu is displayed
19805          * @param {Roo.bootstrap.menu.Menu} this
19806          */
19807         show : true,
19808         /**
19809          * @event hide
19810          * Fires after this menu is hidden
19811          * @param {Roo.bootstrap.menu.Menu} this
19812          */
19813         hide : true,
19814         /**
19815          * @event click
19816          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19817          * @param {Roo.bootstrap.menu.Menu} this
19818          * @param {Roo.EventObject} e
19819          */
19820         click : true
19821     });
19822     
19823 };
19824
19825 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
19826     
19827     submenu : false,
19828     html : '',
19829     weight : 'default',
19830     icon : false,
19831     pos : 'bottom',
19832     
19833     
19834     getChildContainer : function() {
19835         if(this.isSubMenu){
19836             return this.el;
19837         }
19838         
19839         return this.el.select('ul.dropdown-menu', true).first();  
19840     },
19841     
19842     getAutoCreate : function()
19843     {
19844         var text = [
19845             {
19846                 tag : 'span',
19847                 cls : 'roo-menu-text',
19848                 html : this.html
19849             }
19850         ];
19851         
19852         if(this.icon){
19853             text.unshift({
19854                 tag : 'i',
19855                 cls : 'fa ' + this.icon
19856             })
19857         }
19858         
19859         
19860         var cfg = {
19861             tag : 'div',
19862             cls : 'btn-group',
19863             cn : [
19864                 {
19865                     tag : 'button',
19866                     cls : 'dropdown-button btn btn-' + this.weight,
19867                     cn : text
19868                 },
19869                 {
19870                     tag : 'button',
19871                     cls : 'dropdown-toggle btn btn-' + this.weight,
19872                     cn : [
19873                         {
19874                             tag : 'span',
19875                             cls : 'caret'
19876                         }
19877                     ]
19878                 },
19879                 {
19880                     tag : 'ul',
19881                     cls : 'dropdown-menu'
19882                 }
19883             ]
19884             
19885         };
19886         
19887         if(this.pos == 'top'){
19888             cfg.cls += ' dropup';
19889         }
19890         
19891         if(this.isSubMenu){
19892             cfg = {
19893                 tag : 'ul',
19894                 cls : 'dropdown-menu'
19895             }
19896         }
19897         
19898         return cfg;
19899     },
19900     
19901     onRender : function(ct, position)
19902     {
19903         this.isSubMenu = ct.hasClass('dropdown-submenu');
19904         
19905         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
19906     },
19907     
19908     initEvents : function() 
19909     {
19910         if(this.isSubMenu){
19911             return;
19912         }
19913         
19914         this.hidden = true;
19915         
19916         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
19917         this.triggerEl.on('click', this.onTriggerPress, this);
19918         
19919         this.buttonEl = this.el.select('button.dropdown-button', true).first();
19920         this.buttonEl.on('click', this.onClick, this);
19921         
19922     },
19923     
19924     list : function()
19925     {
19926         if(this.isSubMenu){
19927             return this.el;
19928         }
19929         
19930         return this.el.select('ul.dropdown-menu', true).first();
19931     },
19932     
19933     onClick : function(e)
19934     {
19935         this.fireEvent("click", this, e);
19936     },
19937     
19938     onTriggerPress  : function(e)
19939     {   
19940         if (this.isVisible()) {
19941             this.hide();
19942         } else {
19943             this.show();
19944         }
19945     },
19946     
19947     isVisible : function(){
19948         return !this.hidden;
19949     },
19950     
19951     show : function()
19952     {
19953         this.fireEvent("beforeshow", this);
19954         
19955         this.hidden = false;
19956         this.el.addClass('open');
19957         
19958         Roo.get(document).on("mouseup", this.onMouseUp, this);
19959         
19960         this.fireEvent("show", this);
19961         
19962         
19963     },
19964     
19965     hide : function()
19966     {
19967         this.fireEvent("beforehide", this);
19968         
19969         this.hidden = true;
19970         this.el.removeClass('open');
19971         
19972         Roo.get(document).un("mouseup", this.onMouseUp);
19973         
19974         this.fireEvent("hide", this);
19975     },
19976     
19977     onMouseUp : function()
19978     {
19979         this.hide();
19980     }
19981     
19982 });
19983
19984  
19985  /*
19986  * - LGPL
19987  *
19988  * menu item
19989  * 
19990  */
19991 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
19992
19993 /**
19994  * @class Roo.bootstrap.menu.Item
19995  * @extends Roo.bootstrap.Component
19996  * Bootstrap MenuItem class
19997  * @cfg {Boolean} submenu (true | false) default false
19998  * @cfg {String} html text of the item
19999  * @cfg {String} href the link
20000  * @cfg {Boolean} disable (true | false) default false
20001  * @cfg {Boolean} preventDefault (true | false) default true
20002  * @cfg {String} icon Font awesome icon
20003  * @cfg {String} pos Submenu align to (left | right) default right 
20004  * 
20005  * 
20006  * @constructor
20007  * Create a new Item
20008  * @param {Object} config The config object
20009  */
20010
20011
20012 Roo.bootstrap.menu.Item = function(config){
20013     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
20014     this.addEvents({
20015         /**
20016          * @event mouseover
20017          * Fires when the mouse is hovering over this menu
20018          * @param {Roo.bootstrap.menu.Item} this
20019          * @param {Roo.EventObject} e
20020          */
20021         mouseover : true,
20022         /**
20023          * @event mouseout
20024          * Fires when the mouse exits this menu
20025          * @param {Roo.bootstrap.menu.Item} this
20026          * @param {Roo.EventObject} e
20027          */
20028         mouseout : true,
20029         // raw events
20030         /**
20031          * @event click
20032          * The raw click event for the entire grid.
20033          * @param {Roo.EventObject} e
20034          */
20035         click : true
20036     });
20037 };
20038
20039 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
20040     
20041     submenu : false,
20042     href : '',
20043     html : '',
20044     preventDefault: true,
20045     disable : false,
20046     icon : false,
20047     pos : 'right',
20048     
20049     getAutoCreate : function()
20050     {
20051         var text = [
20052             {
20053                 tag : 'span',
20054                 cls : 'roo-menu-item-text',
20055                 html : this.html
20056             }
20057         ];
20058         
20059         if(this.icon){
20060             text.unshift({
20061                 tag : 'i',
20062                 cls : 'fa ' + this.icon
20063             })
20064         }
20065         
20066         var cfg = {
20067             tag : 'li',
20068             cn : [
20069                 {
20070                     tag : 'a',
20071                     href : this.href || '#',
20072                     cn : text
20073                 }
20074             ]
20075         };
20076         
20077         if(this.disable){
20078             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
20079         }
20080         
20081         if(this.submenu){
20082             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
20083             
20084             if(this.pos == 'left'){
20085                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
20086             }
20087         }
20088         
20089         return cfg;
20090     },
20091     
20092     initEvents : function() 
20093     {
20094         this.el.on('mouseover', this.onMouseOver, this);
20095         this.el.on('mouseout', this.onMouseOut, this);
20096         
20097         this.el.select('a', true).first().on('click', this.onClick, this);
20098         
20099     },
20100     
20101     onClick : function(e)
20102     {
20103         if(this.preventDefault){
20104             e.preventDefault();
20105         }
20106         
20107         this.fireEvent("click", this, e);
20108     },
20109     
20110     onMouseOver : function(e)
20111     {
20112         if(this.submenu && this.pos == 'left'){
20113             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
20114         }
20115         
20116         this.fireEvent("mouseover", this, e);
20117     },
20118     
20119     onMouseOut : function(e)
20120     {
20121         this.fireEvent("mouseout", this, e);
20122     }
20123 });
20124
20125  
20126
20127  /*
20128  * - LGPL
20129  *
20130  * menu separator
20131  * 
20132  */
20133 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20134
20135 /**
20136  * @class Roo.bootstrap.menu.Separator
20137  * @extends Roo.bootstrap.Component
20138  * Bootstrap Separator class
20139  * 
20140  * @constructor
20141  * Create a new Separator
20142  * @param {Object} config The config object
20143  */
20144
20145
20146 Roo.bootstrap.menu.Separator = function(config){
20147     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
20148 };
20149
20150 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
20151     
20152     getAutoCreate : function(){
20153         var cfg = {
20154             tag : 'li',
20155             cls: 'divider'
20156         };
20157         
20158         return cfg;
20159     }
20160    
20161 });
20162
20163  
20164
20165