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 or 0 for hidden
774  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
775  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
776  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
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: false,
791     sm: false,
792     md: false,
793     lg: false,
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] !== false) {
808                 if (!settings[size]) { // 0 = hidden
809                     cfg.cls += ' hidden-' + size;
810                     return;
811                 }
812                 cfg.cls += ' col-' + size + '-' + settings[size];
813             }
814         });
815         if (this.html.length) {
816             cfg.html = this.html;
817         }
818         
819         return cfg;
820     }
821    
822 });
823
824  
825
826  /*
827  * - LGPL
828  *
829  * page container.
830  * 
831  */
832
833
834 /**
835  * @class Roo.bootstrap.Container
836  * @extends Roo.bootstrap.Component
837  * Bootstrap Container class
838  * @cfg {Boolean} jumbotron is it a jumbotron element
839  * @cfg {String} html content of element
840  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
841  * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
842  * @cfg {String} header content of header (for panel)
843  * @cfg {String} footer content of footer (for panel)
844  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
845  * @cfg {String} tag (header|aside|section) type of HTML tag.
846
847  *     
848  * @constructor
849  * Create a new Container
850  * @param {Object} config The config object
851  */
852
853 Roo.bootstrap.Container = function(config){
854     Roo.bootstrap.Container.superclass.constructor.call(this, config);
855 };
856
857 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
858     
859     jumbotron : false,
860     well: '',
861     panel : '',
862     header: '',
863     footer : '',
864     sticky: '',
865     tag : false,
866   
867      
868     getChildContainer : function() {
869         
870         if(!this.el){
871             return false;
872         }
873         
874         if (this.panel.length) {
875             return this.el.select('.panel-body',true).first();
876         }
877         
878         return this.el;
879     },
880     
881     
882     getAutoCreate : function(){
883         
884         var cfg = {
885             tag : this.tag || 'div',
886             html : '',
887             cls : ''
888         };
889         if (this.jumbotron) {
890             cfg.cls = 'jumbotron';
891         }
892         // - this is applied by the parent..
893         //if (this.cls) {
894         //    cfg.cls = this.cls + '';
895         //}
896         
897         if (this.sticky.length) {
898             
899             var bd = Roo.get(document.body);
900             if (!bd.hasClass('bootstrap-sticky')) {
901                 bd.addClass('bootstrap-sticky');
902                 Roo.select('html',true).setStyle('height', '100%');
903             }
904              
905             cfg.cls += 'bootstrap-sticky-' + this.sticky;
906         }
907         
908         
909         if (this.well.length) {
910             switch (this.well) {
911                 case 'lg':
912                 case 'sm':
913                     cfg.cls +=' well well-' +this.well;
914                     break;
915                 default:
916                     cfg.cls +=' well';
917                     break;
918             }
919         }
920         
921         var body = cfg;
922         
923         if (this.panel.length) {
924             cfg.cls += ' panel panel-' + this.panel;
925             cfg.cn = [];
926             if (this.header.length) {
927                 cfg.cn.push({
928                     
929                     cls : 'panel-heading',
930                     cn : [{
931                         tag: 'h3',
932                         cls : 'panel-title',
933                         html : this.header
934                     }]
935                     
936                 });
937             }
938             body = false;
939             cfg.cn.push({
940                 cls : 'panel-body',
941                 html : this.html
942             });
943             
944             
945             if (this.footer.length) {
946                 cfg.cn.push({
947                     cls : 'panel-footer',
948                     html : this.footer
949                     
950                 });
951             }
952             
953         }
954         
955         if (body) {
956             body.html = this.html || cfg.html;
957         }
958         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
959             cfg.cls =  'container';
960         }
961         
962         return cfg;
963     },
964     
965     titleEl : function()
966     {
967         if(!this.el || !this.panel.length || !this.header.length){
968             return;
969         }
970         
971         return this.el.select('.panel-title',true).first();
972     },
973     
974     setTitle : function(v)
975     {
976         var titleEl = this.titleEl();
977         
978         if(!titleEl){
979             return;
980         }
981         
982         titleEl.dom.innerHTML = v;
983     },
984     
985     getTitle : function()
986     {
987         
988         var titleEl = this.titleEl();
989         
990         if(!titleEl){
991             return '';
992         }
993         
994         return titleEl.dom.innerHTML;
995     }
996    
997 });
998
999  /*
1000  * - LGPL
1001  *
1002  * image
1003  * 
1004  */
1005
1006
1007 /**
1008  * @class Roo.bootstrap.Img
1009  * @extends Roo.bootstrap.Component
1010  * Bootstrap Img class
1011  * @cfg {Boolean} imgResponsive false | true
1012  * @cfg {String} border rounded | circle | thumbnail
1013  * @cfg {String} src image source
1014  * @cfg {String} alt image alternative text
1015  * @cfg {String} href a tag href
1016  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1017  * 
1018  * @constructor
1019  * Create a new Input
1020  * @param {Object} config The config object
1021  */
1022
1023 Roo.bootstrap.Img = function(config){
1024     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1025     
1026     this.addEvents({
1027         // img events
1028         /**
1029          * @event click
1030          * The img click event for the img.
1031          * @param {Roo.EventObject} e
1032          */
1033         "click" : true
1034     });
1035 };
1036
1037 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1038     
1039     imgResponsive: true,
1040     border: '',
1041     src: '',
1042     href: false,
1043     target: false,
1044
1045     getAutoCreate : function(){
1046         
1047         var cfg = {
1048             tag: 'img',
1049             cls: (this.imgResponsive) ? 'img-responsive' : '',
1050             html : null
1051         }
1052         
1053         cfg.html = this.html || cfg.html;
1054         
1055         cfg.src = this.src || cfg.src;
1056         
1057         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1058             cfg.cls += ' img-' + this.border;
1059         }
1060         
1061         if(this.alt){
1062             cfg.alt = this.alt;
1063         }
1064         
1065         if(this.href){
1066             var a = {
1067                 tag: 'a',
1068                 href: this.href,
1069                 cn: [
1070                     cfg
1071                 ]
1072             }
1073             
1074             if(this.target){
1075                 a.target = this.target;
1076             }
1077             
1078         }
1079         
1080         
1081         return (this.href) ? a : cfg;
1082     },
1083     
1084     initEvents: function() {
1085         
1086         if(!this.href){
1087             this.el.on('click', this.onClick, this);
1088         }
1089     },
1090     
1091     onClick : function(e)
1092     {
1093         Roo.log('img onclick');
1094         this.fireEvent('click', this, e);
1095     }
1096    
1097 });
1098
1099  /*
1100  * - LGPL
1101  *
1102  * image
1103  * 
1104  */
1105
1106
1107 /**
1108  * @class Roo.bootstrap.Link
1109  * @extends Roo.bootstrap.Component
1110  * Bootstrap Link Class
1111  * @cfg {String} alt image alternative text
1112  * @cfg {String} href a tag href
1113  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1114  * @cfg {String} html the content of the link.
1115  * @cfg {Boolean} preventDefault (true | false) default false
1116
1117  * 
1118  * @constructor
1119  * Create a new Input
1120  * @param {Object} config The config object
1121  */
1122
1123 Roo.bootstrap.Link = function(config){
1124     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1125     
1126     this.addEvents({
1127         // img events
1128         /**
1129          * @event click
1130          * The img click event for the img.
1131          * @param {Roo.EventObject} e
1132          */
1133         "click" : true
1134     });
1135 };
1136
1137 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1138     
1139     href: false,
1140     target: false,
1141     preventDefault: false,
1142
1143     getAutoCreate : function(){
1144         
1145         var cfg = {
1146             tag: 'a',
1147             html : this.html || 'html-missing'
1148         }
1149         
1150         
1151         if(this.alt){
1152             cfg.alt = this.alt;
1153         }
1154         cfg.href = this.href || '#';
1155         if(this.target){
1156             cfg.target = this.target;
1157         }
1158         
1159         return cfg;
1160     },
1161     
1162     initEvents: function() {
1163         
1164         if(!this.href || this.preventDefault){
1165             this.el.on('click', this.onClick, this);
1166         }
1167     },
1168     
1169     onClick : function(e)
1170     {
1171         if(this.preventDefault){
1172             e.preventDefault();
1173         }
1174         //Roo.log('img onclick');
1175         this.fireEvent('click', this, e);
1176     }
1177    
1178 });
1179
1180  /*
1181  * - LGPL
1182  *
1183  * header
1184  * 
1185  */
1186
1187 /**
1188  * @class Roo.bootstrap.Header
1189  * @extends Roo.bootstrap.Component
1190  * Bootstrap Header class
1191  * @cfg {String} html content of header
1192  * @cfg {Number} level (1|2|3|4|5|6) default 1
1193  * 
1194  * @constructor
1195  * Create a new Header
1196  * @param {Object} config The config object
1197  */
1198
1199
1200 Roo.bootstrap.Header  = function(config){
1201     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1202 };
1203
1204 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1205     
1206     //href : false,
1207     html : false,
1208     level : 1,
1209     
1210     
1211     
1212     getAutoCreate : function(){
1213         
1214         var cfg = {
1215             tag: 'h' + (1 *this.level),
1216             html: this.html || 'fill in html'
1217         } ;
1218         
1219         return cfg;
1220     }
1221    
1222 });
1223
1224  
1225
1226  /*
1227  * Based on:
1228  * Ext JS Library 1.1.1
1229  * Copyright(c) 2006-2007, Ext JS, LLC.
1230  *
1231  * Originally Released Under LGPL - original licence link has changed is not relivant.
1232  *
1233  * Fork - LGPL
1234  * <script type="text/javascript">
1235  */
1236  
1237 /**
1238  * @class Roo.bootstrap.MenuMgr
1239  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1240  * @singleton
1241  */
1242 Roo.bootstrap.MenuMgr = function(){
1243    var menus, active, groups = {}, attached = false, lastShow = new Date();
1244
1245    // private - called when first menu is created
1246    function init(){
1247        menus = {};
1248        active = new Roo.util.MixedCollection();
1249        Roo.get(document).addKeyListener(27, function(){
1250            if(active.length > 0){
1251                hideAll();
1252            }
1253        });
1254    }
1255
1256    // private
1257    function hideAll(){
1258        if(active && active.length > 0){
1259            var c = active.clone();
1260            c.each(function(m){
1261                m.hide();
1262            });
1263        }
1264    }
1265
1266    // private
1267    function onHide(m){
1268        active.remove(m);
1269        if(active.length < 1){
1270            Roo.get(document).un("mouseup", onMouseDown);
1271             
1272            attached = false;
1273        }
1274    }
1275
1276    // private
1277    function onShow(m){
1278        var last = active.last();
1279        lastShow = new Date();
1280        active.add(m);
1281        if(!attached){
1282           Roo.get(document).on("mouseup", onMouseDown);
1283            
1284            attached = true;
1285        }
1286        if(m.parentMenu){
1287           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1288           m.parentMenu.activeChild = m;
1289        }else if(last && last.isVisible()){
1290           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1291        }
1292    }
1293
1294    // private
1295    function onBeforeHide(m){
1296        if(m.activeChild){
1297            m.activeChild.hide();
1298        }
1299        if(m.autoHideTimer){
1300            clearTimeout(m.autoHideTimer);
1301            delete m.autoHideTimer;
1302        }
1303    }
1304
1305    // private
1306    function onBeforeShow(m){
1307        var pm = m.parentMenu;
1308        if(!pm && !m.allowOtherMenus){
1309            hideAll();
1310        }else if(pm && pm.activeChild && active != m){
1311            pm.activeChild.hide();
1312        }
1313    }
1314
1315    // private
1316    function onMouseDown(e){
1317         Roo.log("on MouseDown");
1318         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
1319            hideAll();
1320         }
1321         
1322         
1323    }
1324
1325    // private
1326    function onBeforeCheck(mi, state){
1327        if(state){
1328            var g = groups[mi.group];
1329            for(var i = 0, l = g.length; i < l; i++){
1330                if(g[i] != mi){
1331                    g[i].setChecked(false);
1332                }
1333            }
1334        }
1335    }
1336
1337    return {
1338
1339        /**
1340         * Hides all menus that are currently visible
1341         */
1342        hideAll : function(){
1343             hideAll();  
1344        },
1345
1346        // private
1347        register : function(menu){
1348            if(!menus){
1349                init();
1350            }
1351            menus[menu.id] = menu;
1352            menu.on("beforehide", onBeforeHide);
1353            menu.on("hide", onHide);
1354            menu.on("beforeshow", onBeforeShow);
1355            menu.on("show", onShow);
1356            var g = menu.group;
1357            if(g && menu.events["checkchange"]){
1358                if(!groups[g]){
1359                    groups[g] = [];
1360                }
1361                groups[g].push(menu);
1362                menu.on("checkchange", onCheck);
1363            }
1364        },
1365
1366         /**
1367          * Returns a {@link Roo.menu.Menu} object
1368          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1369          * be used to generate and return a new Menu instance.
1370          */
1371        get : function(menu){
1372            if(typeof menu == "string"){ // menu id
1373                return menus[menu];
1374            }else if(menu.events){  // menu instance
1375                return menu;
1376            }
1377            /*else if(typeof menu.length == 'number'){ // array of menu items?
1378                return new Roo.bootstrap.Menu({items:menu});
1379            }else{ // otherwise, must be a config
1380                return new Roo.bootstrap.Menu(menu);
1381            }
1382            */
1383            return false;
1384        },
1385
1386        // private
1387        unregister : function(menu){
1388            delete menus[menu.id];
1389            menu.un("beforehide", onBeforeHide);
1390            menu.un("hide", onHide);
1391            menu.un("beforeshow", onBeforeShow);
1392            menu.un("show", onShow);
1393            var g = menu.group;
1394            if(g && menu.events["checkchange"]){
1395                groups[g].remove(menu);
1396                menu.un("checkchange", onCheck);
1397            }
1398        },
1399
1400        // private
1401        registerCheckable : function(menuItem){
1402            var g = menuItem.group;
1403            if(g){
1404                if(!groups[g]){
1405                    groups[g] = [];
1406                }
1407                groups[g].push(menuItem);
1408                menuItem.on("beforecheckchange", onBeforeCheck);
1409            }
1410        },
1411
1412        // private
1413        unregisterCheckable : function(menuItem){
1414            var g = menuItem.group;
1415            if(g){
1416                groups[g].remove(menuItem);
1417                menuItem.un("beforecheckchange", onBeforeCheck);
1418            }
1419        }
1420    };
1421 }();/*
1422  * - LGPL
1423  *
1424  * menu
1425  * 
1426  */
1427
1428 /**
1429  * @class Roo.bootstrap.Menu
1430  * @extends Roo.bootstrap.Component
1431  * Bootstrap Menu class - container for MenuItems
1432  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1433  * 
1434  * @constructor
1435  * Create a new Menu
1436  * @param {Object} config The config object
1437  */
1438
1439
1440 Roo.bootstrap.Menu = function(config){
1441     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1442     if (this.registerMenu) {
1443         Roo.bootstrap.MenuMgr.register(this);
1444     }
1445     this.addEvents({
1446         /**
1447          * @event beforeshow
1448          * Fires before this menu is displayed
1449          * @param {Roo.menu.Menu} this
1450          */
1451         beforeshow : true,
1452         /**
1453          * @event beforehide
1454          * Fires before this menu is hidden
1455          * @param {Roo.menu.Menu} this
1456          */
1457         beforehide : true,
1458         /**
1459          * @event show
1460          * Fires after this menu is displayed
1461          * @param {Roo.menu.Menu} this
1462          */
1463         show : true,
1464         /**
1465          * @event hide
1466          * Fires after this menu is hidden
1467          * @param {Roo.menu.Menu} this
1468          */
1469         hide : true,
1470         /**
1471          * @event click
1472          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1473          * @param {Roo.menu.Menu} this
1474          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1475          * @param {Roo.EventObject} e
1476          */
1477         click : true,
1478         /**
1479          * @event mouseover
1480          * Fires when the mouse is hovering over this menu
1481          * @param {Roo.menu.Menu} this
1482          * @param {Roo.EventObject} e
1483          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1484          */
1485         mouseover : true,
1486         /**
1487          * @event mouseout
1488          * Fires when the mouse exits this menu
1489          * @param {Roo.menu.Menu} this
1490          * @param {Roo.EventObject} e
1491          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1492          */
1493         mouseout : true,
1494         /**
1495          * @event itemclick
1496          * Fires when a menu item contained in this menu is clicked
1497          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1498          * @param {Roo.EventObject} e
1499          */
1500         itemclick: true
1501     });
1502     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1503 };
1504
1505 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1506     
1507    /// html : false,
1508     //align : '',
1509     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1510     type: false,
1511     /**
1512      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1513      */
1514     registerMenu : true,
1515     
1516     menuItems :false, // stores the menu items..
1517     
1518     hidden:true,
1519     
1520     parentMenu : false,
1521     
1522     getChildContainer : function() {
1523         return this.el;  
1524     },
1525     
1526     getAutoCreate : function(){
1527          
1528         //if (['right'].indexOf(this.align)!==-1) {
1529         //    cfg.cn[1].cls += ' pull-right'
1530         //}
1531         
1532         
1533         var cfg = {
1534             tag : 'ul',
1535             cls : 'dropdown-menu' ,
1536             style : 'z-index:1000'
1537             
1538         }
1539         
1540         if (this.type === 'submenu') {
1541             cfg.cls = 'submenu active';
1542         }
1543         if (this.type === 'treeview') {
1544             cfg.cls = 'treeview-menu';
1545         }
1546         
1547         return cfg;
1548     },
1549     initEvents : function() {
1550         
1551        // Roo.log("ADD event");
1552        // Roo.log(this.triggerEl.dom);
1553         this.triggerEl.on('click', this.onTriggerPress, this);
1554         this.triggerEl.addClass('dropdown-toggle');
1555         this.el.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
1556
1557         this.el.on("mouseover", this.onMouseOver, this);
1558         this.el.on("mouseout", this.onMouseOut, this);
1559         
1560         
1561     },
1562     findTargetItem : function(e){
1563         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
1564         if(!t){
1565             return false;
1566         }
1567         //Roo.log(t);         Roo.log(t.id);
1568         if(t && t.id){
1569             //Roo.log(this.menuitems);
1570             return this.menuitems.get(t.id);
1571             
1572             //return this.items.get(t.menuItemId);
1573         }
1574         
1575         return false;
1576     },
1577     onClick : function(e){
1578         Roo.log("menu.onClick");
1579         var t = this.findTargetItem(e);
1580         if(!t){
1581             return;
1582         }
1583         Roo.log(e);
1584         /*
1585         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
1586             if(t == this.activeItem && t.shouldDeactivate(e)){
1587                 this.activeItem.deactivate();
1588                 delete this.activeItem;
1589                 return;
1590             }
1591             if(t.canActivate){
1592                 this.setActiveItem(t, true);
1593             }
1594             return;
1595             
1596             
1597         }
1598         */
1599         Roo.log('pass click event');
1600         
1601         t.onClick(e);
1602         
1603         this.fireEvent("click", this, t, e);
1604         
1605         this.hide();
1606     },
1607      onMouseOver : function(e){
1608         var t  = this.findTargetItem(e);
1609         //Roo.log(t);
1610         //if(t){
1611         //    if(t.canActivate && !t.disabled){
1612         //        this.setActiveItem(t, true);
1613         //    }
1614         //}
1615         
1616         this.fireEvent("mouseover", this, e, t);
1617     },
1618     isVisible : function(){
1619         return !this.hidden;
1620     },
1621      onMouseOut : function(e){
1622         var t  = this.findTargetItem(e);
1623         
1624         //if(t ){
1625         //    if(t == this.activeItem && t.shouldDeactivate(e)){
1626         //        this.activeItem.deactivate();
1627         //        delete this.activeItem;
1628         //    }
1629         //}
1630         this.fireEvent("mouseout", this, e, t);
1631     },
1632     
1633     
1634     /**
1635      * Displays this menu relative to another element
1636      * @param {String/HTMLElement/Roo.Element} element The element to align to
1637      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
1638      * the element (defaults to this.defaultAlign)
1639      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1640      */
1641     show : function(el, pos, parentMenu){
1642         this.parentMenu = parentMenu;
1643         if(!this.el){
1644             this.render();
1645         }
1646         this.fireEvent("beforeshow", this);
1647         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
1648     },
1649      /**
1650      * Displays this menu at a specific xy position
1651      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
1652      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1653      */
1654     showAt : function(xy, parentMenu, /* private: */_e){
1655         this.parentMenu = parentMenu;
1656         if(!this.el){
1657             this.render();
1658         }
1659         if(_e !== false){
1660             this.fireEvent("beforeshow", this);
1661             
1662             //xy = this.el.adjustForConstraints(xy);
1663         }
1664         //this.el.setXY(xy);
1665         //this.el.show();
1666         this.hideMenuItems();
1667         this.hidden = false;
1668         this.triggerEl.addClass('open');
1669         this.focus();
1670         this.fireEvent("show", this);
1671     },
1672     
1673     focus : function(){
1674         return;
1675         if(!this.hidden){
1676             this.doFocus.defer(50, this);
1677         }
1678     },
1679
1680     doFocus : function(){
1681         if(!this.hidden){
1682             this.focusEl.focus();
1683         }
1684     },
1685
1686     /**
1687      * Hides this menu and optionally all parent menus
1688      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
1689      */
1690     hide : function(deep){
1691         
1692         this.hideMenuItems();
1693         if(this.el && this.isVisible()){
1694             this.fireEvent("beforehide", this);
1695             if(this.activeItem){
1696                 this.activeItem.deactivate();
1697                 this.activeItem = null;
1698             }
1699             this.triggerEl.removeClass('open');;
1700             this.hidden = true;
1701             this.fireEvent("hide", this);
1702         }
1703         if(deep === true && this.parentMenu){
1704             this.parentMenu.hide(true);
1705         }
1706     },
1707     
1708     onTriggerPress  : function(e)
1709     {
1710         
1711         Roo.log('trigger press');
1712         //Roo.log(e.getTarget());
1713        // Roo.log(this.triggerEl.dom);
1714         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
1715             return;
1716         }
1717         if (this.isVisible()) {
1718             Roo.log('hide');
1719             this.hide();
1720         } else {
1721             this.show(this.triggerEl, false, false);
1722         }
1723         
1724         
1725     },
1726     
1727          
1728        
1729     
1730     hideMenuItems : function()
1731     {
1732         //$(backdrop).remove()
1733         Roo.select('.open',true).each(function(aa) {
1734             
1735             aa.removeClass('open');
1736           //var parent = getParent($(this))
1737           //var relatedTarget = { relatedTarget: this }
1738           
1739            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
1740           //if (e.isDefaultPrevented()) return
1741            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
1742         })
1743     },
1744     addxtypeChild : function (tree, cntr) {
1745         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
1746           
1747         this.menuitems.add(comp);
1748         return comp;
1749
1750     },
1751     getEl : function()
1752     {
1753         Roo.log(this.el);
1754         return this.el;
1755     }
1756 });
1757
1758  
1759  /*
1760  * - LGPL
1761  *
1762  * menu item
1763  * 
1764  */
1765
1766
1767 /**
1768  * @class Roo.bootstrap.MenuItem
1769  * @extends Roo.bootstrap.Component
1770  * Bootstrap MenuItem class
1771  * @cfg {String} html the menu label
1772  * @cfg {String} href the link
1773  * @cfg {Boolean} preventDefault (true | false) default true
1774  * 
1775  * 
1776  * @constructor
1777  * Create a new MenuItem
1778  * @param {Object} config The config object
1779  */
1780
1781
1782 Roo.bootstrap.MenuItem = function(config){
1783     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
1784     this.addEvents({
1785         // raw events
1786         /**
1787          * @event click
1788          * The raw click event for the entire grid.
1789          * @param {Roo.EventObject} e
1790          */
1791         "click" : true
1792     });
1793 };
1794
1795 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
1796     
1797     href : false,
1798     html : false,
1799     preventDefault: true,
1800     
1801     getAutoCreate : function(){
1802         var cfg= {
1803             tag: 'li',
1804             cls: 'dropdown-menu-item',
1805             cn: [
1806                     {
1807                         tag : 'a',
1808                         href : '#',
1809                         html : 'Link'
1810                     }
1811                 ]
1812         };
1813         if (this.parent().type == 'treeview') {
1814             cfg.cls = 'treeview-menu';
1815         }
1816         
1817         cfg.cn[0].href = this.href || cfg.cn[0].href ;
1818         cfg.cn[0].html = this.html || cfg.cn[0].html ;
1819         return cfg;
1820     },
1821     
1822     initEvents: function() {
1823         
1824         //this.el.select('a').on('click', this.onClick, this);
1825         
1826     },
1827     onClick : function(e)
1828     {
1829         Roo.log('item on click ');
1830         //if(this.preventDefault){
1831         //    e.preventDefault();
1832         //}
1833         //this.parent().hideMenuItems();
1834         
1835         this.fireEvent('click', this, e);
1836     },
1837     getEl : function()
1838     {
1839         return this.el;
1840     }
1841 });
1842
1843  
1844
1845  /*
1846  * - LGPL
1847  *
1848  * menu separator
1849  * 
1850  */
1851
1852
1853 /**
1854  * @class Roo.bootstrap.MenuSeparator
1855  * @extends Roo.bootstrap.Component
1856  * Bootstrap MenuSeparator class
1857  * 
1858  * @constructor
1859  * Create a new MenuItem
1860  * @param {Object} config The config object
1861  */
1862
1863
1864 Roo.bootstrap.MenuSeparator = function(config){
1865     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
1866 };
1867
1868 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
1869     
1870     getAutoCreate : function(){
1871         var cfg = {
1872             cls: 'divider',
1873             tag : 'li'
1874         };
1875         
1876         return cfg;
1877     }
1878    
1879 });
1880
1881  
1882
1883  
1884 /*
1885 <div class="modal fade">
1886   <div class="modal-dialog">
1887     <div class="modal-content">
1888       <div class="modal-header">
1889         <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
1890         <h4 class="modal-title">Modal title</h4>
1891       </div>
1892       <div class="modal-body">
1893         <p>One fine body&hellip;</p>
1894       </div>
1895       <div class="modal-footer">
1896         <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
1897         <button type="button" class="btn btn-primary">Save changes</button>
1898       </div>
1899     </div><!-- /.modal-content -->
1900   </div><!-- /.modal-dialog -->
1901 </div><!-- /.modal -->
1902 */
1903 /*
1904  * - LGPL
1905  *
1906  * page contgainer.
1907  * 
1908  */
1909
1910 /**
1911  * @class Roo.bootstrap.Modal
1912  * @extends Roo.bootstrap.Component
1913  * Bootstrap Modal class
1914  * @cfg {String} title Title of dialog
1915  * @cfg {Boolean} specificTitle (true|false) default false
1916  * @cfg {Array} buttons Array of buttons or standard button set..
1917  * @cfg {String} buttonPosition (left|right|center) default right
1918  * 
1919  * @constructor
1920  * Create a new Modal Dialog
1921  * @param {Object} config The config object
1922  */
1923
1924 Roo.bootstrap.Modal = function(config){
1925     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
1926     this.addEvents({
1927         // raw events
1928         /**
1929          * @event btnclick
1930          * The raw btnclick event for the button
1931          * @param {Roo.EventObject} e
1932          */
1933         "btnclick" : true
1934     });
1935     this.buttons = this.buttons || [];
1936 };
1937
1938 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
1939     
1940     title : 'test dialog',
1941    
1942     buttons : false,
1943     
1944     // set on load...
1945     body:  false,
1946     
1947     specificTitle: false,
1948     
1949     buttonPosition: 'right',
1950     
1951     onRender : function(ct, position)
1952     {
1953         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
1954      
1955         if(!this.el){
1956             var cfg = Roo.apply({},  this.getAutoCreate());
1957             cfg.id = Roo.id();
1958             //if(!cfg.name){
1959             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
1960             //}
1961             //if (!cfg.name.length) {
1962             //    delete cfg.name;
1963            // }
1964             if (this.cls) {
1965                 cfg.cls += ' ' + this.cls;
1966             }
1967             if (this.style) {
1968                 cfg.style = this.style;
1969             }
1970             this.el = Roo.get(document.body).createChild(cfg, position);
1971         }
1972         //var type = this.el.dom.type;
1973         
1974         if(this.tabIndex !== undefined){
1975             this.el.dom.setAttribute('tabIndex', this.tabIndex);
1976         }
1977         
1978         
1979         
1980         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
1981         this.maskEl.enableDisplayMode("block");
1982         this.maskEl.hide();
1983         //this.el.addClass("x-dlg-modal");
1984     
1985         if (this.buttons.length) {
1986             Roo.each(this.buttons, function(bb) {
1987                 b = Roo.apply({}, bb);
1988                 b.xns = b.xns || Roo.bootstrap;
1989                 b.xtype = b.xtype || 'Button';
1990                 if (typeof(b.listeners) == 'undefined') {
1991                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
1992                 }
1993                 
1994                 var btn = Roo.factory(b);
1995                 
1996                 btn.onRender(this.el.select('.modal-footer div').first());
1997                 
1998             },this);
1999         }
2000         // render the children.
2001         var nitems = [];
2002         
2003         if(typeof(this.items) != 'undefined'){
2004             var items = this.items;
2005             delete this.items;
2006
2007             for(var i =0;i < items.length;i++) {
2008                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2009             }
2010         }
2011         
2012         this.items = nitems;
2013         
2014         this.body = this.el.select('.modal-body',true).first();
2015         this.close = this.el.select('.modal-header .close', true).first();
2016         this.footer = this.el.select('.modal-footer',true).first();
2017         this.initEvents();
2018         //this.el.addClass([this.fieldClass, this.cls]);
2019         
2020     },
2021     getAutoCreate : function(){
2022         
2023         
2024         var bdy = {
2025                 cls : 'modal-body',
2026                 html : this.html || ''
2027         };
2028         
2029         var title = {
2030             tag: 'h4',
2031             cls : 'modal-title',
2032             html : this.title
2033         };
2034         
2035         if(this.specificTitle){
2036             title = this.title;
2037         };
2038         
2039         return modal = {
2040             cls: "modal fade",
2041             style : 'display: none',
2042             cn : [
2043                 {
2044                     cls: "modal-dialog",
2045                     cn : [
2046                         {
2047                             cls : "modal-content",
2048                             cn : [
2049                                 {
2050                                     cls : 'modal-header',
2051                                     cn : [
2052                                         {
2053                                             tag: 'button',
2054                                             cls : 'close',
2055                                             html : '&times'
2056                                         },
2057                                         title
2058                                     ]
2059                                 },
2060                                 bdy,
2061                                 {
2062                                     cls : 'modal-footer',
2063                                     cn : [
2064                                         {
2065                                             tag: 'div',
2066                                             cls: 'btn-' + this.buttonPosition
2067                                         }
2068                                     ]
2069                                     
2070                                 }
2071                                 
2072                                 
2073                             ]
2074                             
2075                         }
2076                     ]
2077                         
2078                 }
2079             ]
2080             
2081             
2082         };
2083           
2084     },
2085     getChildContainer : function() {
2086          
2087          return this.el.select('.modal-body',true).first();
2088         
2089     },
2090     getButtonContainer : function() {
2091          return this.el.select('.modal-footer div',true).first();
2092         
2093     },
2094     initEvents : function()
2095     {
2096         this.el.select('.modal-header .close').on('click', this.hide, this);
2097 //        
2098 //        this.addxtype(this);
2099     },
2100     show : function() {
2101         
2102         if (!this.rendered) {
2103             this.render();
2104         }
2105        
2106         this.el.addClass('on');
2107         this.el.removeClass('fade');
2108         this.el.setStyle('display', 'block');
2109         Roo.get(document.body).addClass("x-body-masked");
2110         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2111         this.maskEl.show();
2112         this.el.setStyle('zIndex', '10001');
2113         this.fireEvent('show', this);
2114         
2115         
2116     },
2117     hide : function()
2118     {
2119         Roo.log('Modal hide?!');
2120         this.maskEl.hide();
2121         Roo.get(document.body).removeClass("x-body-masked");
2122         this.el.removeClass('on');
2123         this.el.addClass('fade');
2124         this.el.setStyle('display', 'none');
2125         this.fireEvent('hide', this);
2126     },
2127     
2128     addButton : function(str, cb)
2129     {
2130          
2131         
2132         var b = Roo.apply({}, { html : str } );
2133         b.xns = b.xns || Roo.bootstrap;
2134         b.xtype = b.xtype || 'Button';
2135         if (typeof(b.listeners) == 'undefined') {
2136             b.listeners = { click : cb.createDelegate(this)  };
2137         }
2138         
2139         var btn = Roo.factory(b);
2140            
2141         btn.onRender(this.el.select('.modal-footer div').first());
2142         
2143         return btn;   
2144        
2145     },
2146     
2147     setDefaultButton : function(btn)
2148     {
2149         //this.el.select('.modal-footer').()
2150     },
2151     resizeTo: function(w,h)
2152     {
2153         // skip..
2154     },
2155     setContentSize  : function(w, h)
2156     {
2157         
2158     },
2159     onButtonClick: function(btn,e)
2160     {
2161         //Roo.log([a,b,c]);
2162         this.fireEvent('btnclick', btn.name, e);
2163     },
2164     setTitle: function(str) {
2165         this.el.select('.modal-title',true).first().dom.innerHTML = str;
2166         
2167     }
2168 });
2169
2170
2171 Roo.apply(Roo.bootstrap.Modal,  {
2172     /**
2173          * Button config that displays a single OK button
2174          * @type Object
2175          */
2176         OK :  [{
2177             name : 'ok',
2178             weight : 'primary',
2179             html : 'OK'
2180         }], 
2181         /**
2182          * Button config that displays Yes and No buttons
2183          * @type Object
2184          */
2185         YESNO : [
2186             {
2187                 name  : 'no',
2188                 html : 'No'
2189             },
2190             {
2191                 name  :'yes',
2192                 weight : 'primary',
2193                 html : 'Yes'
2194             }
2195         ],
2196         
2197         /**
2198          * Button config that displays OK and Cancel buttons
2199          * @type Object
2200          */
2201         OKCANCEL : [
2202             {
2203                name : 'cancel',
2204                 html : 'Cancel'
2205             },
2206             {
2207                 name : 'ok',
2208                 weight : 'primary',
2209                 html : 'OK'
2210             }
2211         ],
2212         /**
2213          * Button config that displays Yes, No and Cancel buttons
2214          * @type Object
2215          */
2216         YESNOCANCEL : [
2217             {
2218                 name : 'yes',
2219                 weight : 'primary',
2220                 html : 'Yes'
2221             },
2222             {
2223                 name : 'no',
2224                 html : 'No'
2225             },
2226             {
2227                 name : 'cancel',
2228                 html : 'Cancel'
2229             }
2230         ]
2231 });
2232  /*
2233  * - LGPL
2234  *
2235  * messagebox - can be used as a replace
2236  * 
2237  */
2238 /**
2239  * @class Roo.MessageBox
2240  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2241  * Example usage:
2242  *<pre><code>
2243 // Basic alert:
2244 Roo.Msg.alert('Status', 'Changes saved successfully.');
2245
2246 // Prompt for user data:
2247 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2248     if (btn == 'ok'){
2249         // process text value...
2250     }
2251 });
2252
2253 // Show a dialog using config options:
2254 Roo.Msg.show({
2255    title:'Save Changes?',
2256    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2257    buttons: Roo.Msg.YESNOCANCEL,
2258    fn: processResult,
2259    animEl: 'elId'
2260 });
2261 </code></pre>
2262  * @singleton
2263  */
2264 Roo.bootstrap.MessageBox = function(){
2265     var dlg, opt, mask, waitTimer;
2266     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2267     var buttons, activeTextEl, bwidth;
2268
2269     
2270     // private
2271     var handleButton = function(button){
2272         dlg.hide();
2273         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2274     };
2275
2276     // private
2277     var handleHide = function(){
2278         if(opt && opt.cls){
2279             dlg.el.removeClass(opt.cls);
2280         }
2281         //if(waitTimer){
2282         //    Roo.TaskMgr.stop(waitTimer);
2283         //    waitTimer = null;
2284         //}
2285     };
2286
2287     // private
2288     var updateButtons = function(b){
2289         var width = 0;
2290         if(!b){
2291             buttons["ok"].hide();
2292             buttons["cancel"].hide();
2293             buttons["yes"].hide();
2294             buttons["no"].hide();
2295             //dlg.footer.dom.style.display = 'none';
2296             return width;
2297         }
2298         dlg.footer.dom.style.display = '';
2299         for(var k in buttons){
2300             if(typeof buttons[k] != "function"){
2301                 if(b[k]){
2302                     buttons[k].show();
2303                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2304                     width += buttons[k].el.getWidth()+15;
2305                 }else{
2306                     buttons[k].hide();
2307                 }
2308             }
2309         }
2310         return width;
2311     };
2312
2313     // private
2314     var handleEsc = function(d, k, e){
2315         if(opt && opt.closable !== false){
2316             dlg.hide();
2317         }
2318         if(e){
2319             e.stopEvent();
2320         }
2321     };
2322
2323     return {
2324         /**
2325          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2326          * @return {Roo.BasicDialog} The BasicDialog element
2327          */
2328         getDialog : function(){
2329            if(!dlg){
2330                 dlg = new Roo.bootstrap.Modal( {
2331                     //draggable: true,
2332                     //resizable:false,
2333                     //constraintoviewport:false,
2334                     //fixedcenter:true,
2335                     //collapsible : false,
2336                     //shim:true,
2337                     //modal: true,
2338                   //  width:400,
2339                   //  height:100,
2340                     //buttonAlign:"center",
2341                     closeClick : function(){
2342                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2343                             handleButton("no");
2344                         }else{
2345                             handleButton("cancel");
2346                         }
2347                     }
2348                 });
2349                 dlg.render();
2350                 dlg.on("hide", handleHide);
2351                 mask = dlg.mask;
2352                 //dlg.addKeyListener(27, handleEsc);
2353                 buttons = {};
2354                 this.buttons = buttons;
2355                 var bt = this.buttonText;
2356                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2357                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2358                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2359                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2360                 Roo.log(buttons)
2361                 bodyEl = dlg.body.createChild({
2362
2363                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2364                         '<textarea class="roo-mb-textarea"></textarea>' +
2365                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
2366                 });
2367                 msgEl = bodyEl.dom.firstChild;
2368                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2369                 textboxEl.enableDisplayMode();
2370                 textboxEl.addKeyListener([10,13], function(){
2371                     if(dlg.isVisible() && opt && opt.buttons){
2372                         if(opt.buttons.ok){
2373                             handleButton("ok");
2374                         }else if(opt.buttons.yes){
2375                             handleButton("yes");
2376                         }
2377                     }
2378                 });
2379                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2380                 textareaEl.enableDisplayMode();
2381                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2382                 progressEl.enableDisplayMode();
2383                 var pf = progressEl.dom.firstChild;
2384                 if (pf) {
2385                     pp = Roo.get(pf.firstChild);
2386                     pp.setHeight(pf.offsetHeight);
2387                 }
2388                 
2389             }
2390             return dlg;
2391         },
2392
2393         /**
2394          * Updates the message box body text
2395          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2396          * the XHTML-compliant non-breaking space character '&amp;#160;')
2397          * @return {Roo.MessageBox} This message box
2398          */
2399         updateText : function(text){
2400             if(!dlg.isVisible() && !opt.width){
2401                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2402             }
2403             msgEl.innerHTML = text || '&#160;';
2404       
2405             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2406             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2407             var w = Math.max(
2408                     Math.min(opt.width || cw , this.maxWidth), 
2409                     Math.max(opt.minWidth || this.minWidth, bwidth)
2410             );
2411             if(opt.prompt){
2412                 activeTextEl.setWidth(w);
2413             }
2414             if(dlg.isVisible()){
2415                 dlg.fixedcenter = false;
2416             }
2417             // to big, make it scroll. = But as usual stupid IE does not support
2418             // !important..
2419             
2420             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2421                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2422                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2423             } else {
2424                 bodyEl.dom.style.height = '';
2425                 bodyEl.dom.style.overflowY = '';
2426             }
2427             if (cw > w) {
2428                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2429             } else {
2430                 bodyEl.dom.style.overflowX = '';
2431             }
2432             
2433             dlg.setContentSize(w, bodyEl.getHeight());
2434             if(dlg.isVisible()){
2435                 dlg.fixedcenter = true;
2436             }
2437             return this;
2438         },
2439
2440         /**
2441          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
2442          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2443          * @param {Number} value Any number between 0 and 1 (e.g., .5)
2444          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2445          * @return {Roo.MessageBox} This message box
2446          */
2447         updateProgress : function(value, text){
2448             if(text){
2449                 this.updateText(text);
2450             }
2451             if (pp) { // weird bug on my firefox - for some reason this is not defined
2452                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2453             }
2454             return this;
2455         },        
2456
2457         /**
2458          * Returns true if the message box is currently displayed
2459          * @return {Boolean} True if the message box is visible, else false
2460          */
2461         isVisible : function(){
2462             return dlg && dlg.isVisible();  
2463         },
2464
2465         /**
2466          * Hides the message box if it is displayed
2467          */
2468         hide : function(){
2469             if(this.isVisible()){
2470                 dlg.hide();
2471             }  
2472         },
2473
2474         /**
2475          * Displays a new message box, or reinitializes an existing message box, based on the config options
2476          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2477          * The following config object properties are supported:
2478          * <pre>
2479 Property    Type             Description
2480 ----------  ---------------  ------------------------------------------------------------------------------------
2481 animEl            String/Element   An id or Element from which the message box should animate as it opens and
2482                                    closes (defaults to undefined)
2483 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
2484                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
2485 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
2486                                    progress and wait dialogs will ignore this property and always hide the
2487                                    close button as they can only be closed programmatically.
2488 cls               String           A custom CSS class to apply to the message box element
2489 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
2490                                    displayed (defaults to 75)
2491 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
2492                                    function will be btn (the name of the button that was clicked, if applicable,
2493                                    e.g. "ok"), and text (the value of the active text field, if applicable).
2494                                    Progress and wait dialogs will ignore this option since they do not respond to
2495                                    user actions and can only be closed programmatically, so any required function
2496                                    should be called by the same code after it closes the dialog.
2497 icon              String           A CSS class that provides a background image to be used as an icon for
2498                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
2499 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
2500 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
2501 modal             Boolean          False to allow user interaction with the page while the message box is
2502                                    displayed (defaults to true)
2503 msg               String           A string that will replace the existing message box body text (defaults
2504                                    to the XHTML-compliant non-breaking space character '&#160;')
2505 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
2506 progress          Boolean          True to display a progress bar (defaults to false)
2507 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
2508 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
2509 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
2510 title             String           The title text
2511 value             String           The string value to set into the active textbox element if displayed
2512 wait              Boolean          True to display a progress bar (defaults to false)
2513 width             Number           The width of the dialog in pixels
2514 </pre>
2515          *
2516          * Example usage:
2517          * <pre><code>
2518 Roo.Msg.show({
2519    title: 'Address',
2520    msg: 'Please enter your address:',
2521    width: 300,
2522    buttons: Roo.MessageBox.OKCANCEL,
2523    multiline: true,
2524    fn: saveAddress,
2525    animEl: 'addAddressBtn'
2526 });
2527 </code></pre>
2528          * @param {Object} config Configuration options
2529          * @return {Roo.MessageBox} This message box
2530          */
2531         show : function(options)
2532         {
2533             
2534             // this causes nightmares if you show one dialog after another
2535             // especially on callbacks..
2536              
2537             if(this.isVisible()){
2538                 
2539                 this.hide();
2540                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
2541                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
2542                 Roo.log("New Dialog Message:" +  options.msg )
2543                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
2544                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
2545                 
2546             }
2547             var d = this.getDialog();
2548             opt = options;
2549             d.setTitle(opt.title || "&#160;");
2550             d.close.setDisplayed(opt.closable !== false);
2551             activeTextEl = textboxEl;
2552             opt.prompt = opt.prompt || (opt.multiline ? true : false);
2553             if(opt.prompt){
2554                 if(opt.multiline){
2555                     textboxEl.hide();
2556                     textareaEl.show();
2557                     textareaEl.setHeight(typeof opt.multiline == "number" ?
2558                         opt.multiline : this.defaultTextHeight);
2559                     activeTextEl = textareaEl;
2560                 }else{
2561                     textboxEl.show();
2562                     textareaEl.hide();
2563                 }
2564             }else{
2565                 textboxEl.hide();
2566                 textareaEl.hide();
2567             }
2568             progressEl.setDisplayed(opt.progress === true);
2569             this.updateProgress(0);
2570             activeTextEl.dom.value = opt.value || "";
2571             if(opt.prompt){
2572                 dlg.setDefaultButton(activeTextEl);
2573             }else{
2574                 var bs = opt.buttons;
2575                 var db = null;
2576                 if(bs && bs.ok){
2577                     db = buttons["ok"];
2578                 }else if(bs && bs.yes){
2579                     db = buttons["yes"];
2580                 }
2581                 dlg.setDefaultButton(db);
2582             }
2583             bwidth = updateButtons(opt.buttons);
2584             this.updateText(opt.msg);
2585             if(opt.cls){
2586                 d.el.addClass(opt.cls);
2587             }
2588             d.proxyDrag = opt.proxyDrag === true;
2589             d.modal = opt.modal !== false;
2590             d.mask = opt.modal !== false ? mask : false;
2591             if(!d.isVisible()){
2592                 // force it to the end of the z-index stack so it gets a cursor in FF
2593                 document.body.appendChild(dlg.el.dom);
2594                 d.animateTarget = null;
2595                 d.show(options.animEl);
2596             }
2597             return this;
2598         },
2599
2600         /**
2601          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
2602          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
2603          * and closing the message box when the process is complete.
2604          * @param {String} title The title bar text
2605          * @param {String} msg The message box body text
2606          * @return {Roo.MessageBox} This message box
2607          */
2608         progress : function(title, msg){
2609             this.show({
2610                 title : title,
2611                 msg : msg,
2612                 buttons: false,
2613                 progress:true,
2614                 closable:false,
2615                 minWidth: this.minProgressWidth,
2616                 modal : true
2617             });
2618             return this;
2619         },
2620
2621         /**
2622          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
2623          * If a callback function is passed it will be called after the user clicks the button, and the
2624          * id of the button that was clicked will be passed as the only parameter to the callback
2625          * (could also be the top-right close button).
2626          * @param {String} title The title bar text
2627          * @param {String} msg The message box body text
2628          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2629          * @param {Object} scope (optional) The scope of the callback function
2630          * @return {Roo.MessageBox} This message box
2631          */
2632         alert : function(title, msg, fn, scope){
2633             this.show({
2634                 title : title,
2635                 msg : msg,
2636                 buttons: this.OK,
2637                 fn: fn,
2638                 scope : scope,
2639                 modal : true
2640             });
2641             return this;
2642         },
2643
2644         /**
2645          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
2646          * interaction while waiting for a long-running process to complete that does not have defined intervals.
2647          * You are responsible for closing the message box when the process is complete.
2648          * @param {String} msg The message box body text
2649          * @param {String} title (optional) The title bar text
2650          * @return {Roo.MessageBox} This message box
2651          */
2652         wait : function(msg, title){
2653             this.show({
2654                 title : title,
2655                 msg : msg,
2656                 buttons: false,
2657                 closable:false,
2658                 progress:true,
2659                 modal:true,
2660                 width:300,
2661                 wait:true
2662             });
2663             waitTimer = Roo.TaskMgr.start({
2664                 run: function(i){
2665                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
2666                 },
2667                 interval: 1000
2668             });
2669             return this;
2670         },
2671
2672         /**
2673          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
2674          * If a callback function is passed it will be called after the user clicks either button, and the id of the
2675          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
2676          * @param {String} title The title bar text
2677          * @param {String} msg The message box body text
2678          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2679          * @param {Object} scope (optional) The scope of the callback function
2680          * @return {Roo.MessageBox} This message box
2681          */
2682         confirm : function(title, msg, fn, scope){
2683             this.show({
2684                 title : title,
2685                 msg : msg,
2686                 buttons: this.YESNO,
2687                 fn: fn,
2688                 scope : scope,
2689                 modal : true
2690             });
2691             return this;
2692         },
2693
2694         /**
2695          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
2696          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
2697          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
2698          * (could also be the top-right close button) and the text that was entered will be passed as the two
2699          * parameters to the callback.
2700          * @param {String} title The title bar text
2701          * @param {String} msg The message box body text
2702          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2703          * @param {Object} scope (optional) The scope of the callback function
2704          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
2705          * property, or the height in pixels to create the textbox (defaults to false / single-line)
2706          * @return {Roo.MessageBox} This message box
2707          */
2708         prompt : function(title, msg, fn, scope, multiline){
2709             this.show({
2710                 title : title,
2711                 msg : msg,
2712                 buttons: this.OKCANCEL,
2713                 fn: fn,
2714                 minWidth:250,
2715                 scope : scope,
2716                 prompt:true,
2717                 multiline: multiline,
2718                 modal : true
2719             });
2720             return this;
2721         },
2722
2723         /**
2724          * Button config that displays a single OK button
2725          * @type Object
2726          */
2727         OK : {ok:true},
2728         /**
2729          * Button config that displays Yes and No buttons
2730          * @type Object
2731          */
2732         YESNO : {yes:true, no:true},
2733         /**
2734          * Button config that displays OK and Cancel buttons
2735          * @type Object
2736          */
2737         OKCANCEL : {ok:true, cancel:true},
2738         /**
2739          * Button config that displays Yes, No and Cancel buttons
2740          * @type Object
2741          */
2742         YESNOCANCEL : {yes:true, no:true, cancel:true},
2743
2744         /**
2745          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
2746          * @type Number
2747          */
2748         defaultTextHeight : 75,
2749         /**
2750          * The maximum width in pixels of the message box (defaults to 600)
2751          * @type Number
2752          */
2753         maxWidth : 600,
2754         /**
2755          * The minimum width in pixels of the message box (defaults to 100)
2756          * @type Number
2757          */
2758         minWidth : 100,
2759         /**
2760          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
2761          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
2762          * @type Number
2763          */
2764         minProgressWidth : 250,
2765         /**
2766          * An object containing the default button text strings that can be overriden for localized language support.
2767          * Supported properties are: ok, cancel, yes and no.
2768          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
2769          * @type Object
2770          */
2771         buttonText : {
2772             ok : "OK",
2773             cancel : "Cancel",
2774             yes : "Yes",
2775             no : "No"
2776         }
2777     };
2778 }();
2779
2780 /**
2781  * Shorthand for {@link Roo.MessageBox}
2782  */
2783 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox 
2784 Roo.Msg = Roo.Msg || Roo.MessageBox;
2785 /*
2786  * - LGPL
2787  *
2788  * navbar
2789  * 
2790  */
2791
2792 /**
2793  * @class Roo.bootstrap.Navbar
2794  * @extends Roo.bootstrap.Component
2795  * Bootstrap Navbar class
2796
2797  * @constructor
2798  * Create a new Navbar
2799  * @param {Object} config The config object
2800  */
2801
2802
2803 Roo.bootstrap.Navbar = function(config){
2804     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
2805     
2806 };
2807
2808 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
2809     
2810     
2811    
2812     // private
2813     navItems : false,
2814     loadMask : false,
2815     
2816     
2817     getAutoCreate : function(){
2818         
2819         
2820         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
2821         
2822     },
2823     
2824     initEvents :function ()
2825     {
2826         //Roo.log(this.el.select('.navbar-toggle',true));
2827         this.el.select('.navbar-toggle',true).on('click', function() {
2828            // Roo.log('click');
2829             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
2830         }, this);
2831         
2832         var mark = {
2833             tag: "div",
2834             cls:"x-dlg-mask"
2835         }
2836         
2837         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
2838         
2839         var size = this.el.getSize();
2840         this.maskEl.setSize(size.width, size.height);
2841         this.maskEl.enableDisplayMode("block");
2842         this.maskEl.hide();
2843         
2844         if(this.loadMask){
2845             this.maskEl.show();
2846         }
2847     },
2848     
2849     
2850     getChildContainer : function()
2851     {
2852         if (this.el.select('.collapse').getCount()) {
2853             return this.el.select('.collapse',true).first();
2854         }
2855         
2856         return this.el;
2857     },
2858     
2859     mask : function()
2860     {
2861         this.maskEl.show();
2862     },
2863     
2864     unmask : function()
2865     {
2866         this.maskEl.hide();
2867     } 
2868     
2869     
2870     
2871     
2872 });
2873
2874
2875
2876  
2877
2878  /*
2879  * - LGPL
2880  *
2881  * navbar
2882  * 
2883  */
2884
2885 /**
2886  * @class Roo.bootstrap.NavSimplebar
2887  * @extends Roo.bootstrap.Navbar
2888  * Bootstrap Sidebar class
2889  *
2890  * @cfg {Boolean} inverse is inverted color
2891  * 
2892  * @cfg {String} type (nav | pills | tabs)
2893  * @cfg {Boolean} arrangement stacked | justified
2894  * @cfg {String} align (left | right) alignment
2895  * 
2896  * @cfg {Boolean} main (true|false) main nav bar? default false
2897  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
2898  * 
2899  * @cfg {String} tag (header|footer|nav|div) default is nav 
2900
2901  * 
2902  * 
2903  * 
2904  * @constructor
2905  * Create a new Sidebar
2906  * @param {Object} config The config object
2907  */
2908
2909
2910 Roo.bootstrap.NavSimplebar = function(config){
2911     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
2912 };
2913
2914 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
2915     
2916     inverse: false,
2917     
2918     type: false,
2919     arrangement: '',
2920     align : false,
2921     
2922     
2923     
2924     main : false,
2925     
2926     
2927     tag : false,
2928     
2929     
2930     getAutoCreate : function(){
2931         
2932         
2933         var cfg = {
2934             tag : this.tag || 'div',
2935             cls : 'navbar'
2936         };
2937           
2938         
2939         cfg.cn = [
2940             {
2941                 cls: 'nav',
2942                 tag : 'ul'
2943             }
2944         ];
2945         
2946          
2947         this.type = this.type || 'nav';
2948         if (['tabs','pills'].indexOf(this.type)!==-1) {
2949             cfg.cn[0].cls += ' nav-' + this.type
2950         
2951         
2952         } else {
2953             if (this.type!=='nav') {
2954                 Roo.log('nav type must be nav/tabs/pills')
2955             }
2956             cfg.cn[0].cls += ' navbar-nav'
2957         }
2958         
2959         
2960         
2961         
2962         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
2963             cfg.cn[0].cls += ' nav-' + this.arrangement;
2964         }
2965         
2966         
2967         if (this.align === 'right') {
2968             cfg.cn[0].cls += ' navbar-right';
2969         }
2970         
2971         if (this.inverse) {
2972             cfg.cls += ' navbar-inverse';
2973             
2974         }
2975         
2976         
2977         return cfg;
2978     
2979         
2980     }
2981     
2982     
2983     
2984 });
2985
2986
2987
2988  
2989
2990  
2991        /*
2992  * - LGPL
2993  *
2994  * navbar
2995  * 
2996  */
2997
2998 /**
2999  * @class Roo.bootstrap.NavHeaderbar
3000  * @extends Roo.bootstrap.NavSimplebar
3001  * Bootstrap Sidebar class
3002  *
3003  * @cfg {String} brand what is brand
3004  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3005  * @cfg {String} brand_href href of the brand
3006  * @cfg {Boolean} srButton generate the sr-only button (true | false) default true
3007  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3008  * 
3009  * @constructor
3010  * Create a new Sidebar
3011  * @param {Object} config The config object
3012  */
3013
3014
3015 Roo.bootstrap.NavHeaderbar = function(config){
3016     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3017 };
3018
3019 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3020     
3021     position: '',
3022     brand: '',
3023     brand_href: false,
3024     srButton : true,
3025     autohide : false,
3026     
3027     getAutoCreate : function(){
3028         
3029         var   cfg = {
3030             tag: this.nav || 'nav',
3031             cls: 'navbar',
3032             role: 'navigation',
3033             cn: []
3034         };
3035         
3036         if(this.srButton){
3037             cfg.cn.push({
3038                 tag: 'div',
3039                 cls: 'navbar-header',
3040                 cn: [
3041                     {
3042                         tag: 'button',
3043                         type: 'button',
3044                         cls: 'navbar-toggle',
3045                         'data-toggle': 'collapse',
3046                         cn: [
3047                             {
3048                                 tag: 'span',
3049                                 cls: 'sr-only',
3050                                 html: 'Toggle navigation'
3051                             },
3052                             {
3053                                 tag: 'span',
3054                                 cls: 'icon-bar'
3055                             },
3056                             {
3057                                 tag: 'span',
3058                                 cls: 'icon-bar'
3059                             },
3060                             {
3061                                 tag: 'span',
3062                                 cls: 'icon-bar'
3063                             }
3064                         ]
3065                     }
3066                 ]
3067             });
3068         }
3069         
3070         cfg.cn.push({
3071             tag: 'div',
3072             cls: 'collapse navbar-collapse',
3073             cn : []
3074         });
3075         
3076         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3077         
3078         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3079             cfg.cls += ' navbar-' + this.position;
3080             
3081             // tag can override this..
3082             
3083             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3084         }
3085         
3086         if (this.brand !== '') {
3087             cfg.cn[0].cn.push({
3088                 tag: 'a',
3089                 href: this.brand_href ? this.brand_href : '#',
3090                 cls: 'navbar-brand',
3091                 cn: [
3092                 this.brand
3093                 ]
3094             });
3095         }
3096         
3097         if(this.main){
3098             cfg.cls += ' main-nav';
3099         }
3100         
3101         
3102         return cfg;
3103
3104         
3105     },
3106     initEvents : function()
3107     {
3108         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3109         
3110         if (this.autohide) {
3111             
3112             var prevScroll = 0;
3113             var ft = this.el;
3114             
3115             Roo.get(document).on('scroll',function(e) {
3116                 var ns = Roo.get(document).getScroll().top;
3117                 var os = prevScroll;
3118                 prevScroll = ns;
3119                 
3120                 if(ns > os){
3121                     ft.removeClass('slideDown');
3122                     ft.addClass('slideUp');
3123                     return;
3124                 }
3125                 ft.removeClass('slideUp');
3126                 ft.addClass('slideDown');
3127                  
3128               
3129           },this);
3130         }
3131     }    
3132           
3133       
3134     
3135     
3136 });
3137
3138
3139
3140  
3141
3142  /*
3143  * - LGPL
3144  *
3145  * navbar
3146  * 
3147  */
3148
3149 /**
3150  * @class Roo.bootstrap.NavSidebar
3151  * @extends Roo.bootstrap.Navbar
3152  * Bootstrap Sidebar class
3153  * 
3154  * @constructor
3155  * Create a new Sidebar
3156  * @param {Object} config The config object
3157  */
3158
3159
3160 Roo.bootstrap.NavSidebar = function(config){
3161     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3162 };
3163
3164 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3165     
3166     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3167     
3168     getAutoCreate : function(){
3169         
3170         
3171         return  {
3172             tag: 'div',
3173             cls: 'sidebar sidebar-nav'
3174         };
3175     
3176         
3177     }
3178     
3179     
3180     
3181 });
3182
3183
3184
3185  
3186
3187  /*
3188  * - LGPL
3189  *
3190  * nav group
3191  * 
3192  */
3193
3194 /**
3195  * @class Roo.bootstrap.NavGroup
3196  * @extends Roo.bootstrap.Component
3197  * Bootstrap NavGroup class
3198  * @cfg {String} align left | right
3199  * @cfg {Boolean} inverse false | true
3200  * @cfg {String} type (nav|pills|tab) default nav
3201  * @cfg {String} navId - reference Id for navbar.
3202
3203  * 
3204  * @constructor
3205  * Create a new nav group
3206  * @param {Object} config The config object
3207  */
3208
3209 Roo.bootstrap.NavGroup = function(config){
3210     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3211     this.navItems = [];
3212    
3213     Roo.bootstrap.NavGroup.register(this);
3214      this.addEvents({
3215         /**
3216              * @event changed
3217              * Fires when the active item changes
3218              * @param {Roo.bootstrap.NavGroup} this
3219              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3220              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3221          */
3222         'changed': true
3223      });
3224     
3225 };
3226
3227 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3228     
3229     align: '',
3230     inverse: false,
3231     form: false,
3232     type: 'nav',
3233     navId : '',
3234     // private
3235     
3236     navItems : false, 
3237     
3238     getAutoCreate : function()
3239     {
3240         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3241         
3242         cfg = {
3243             tag : 'ul',
3244             cls: 'nav' 
3245         }
3246         
3247         if (['tabs','pills'].indexOf(this.type)!==-1) {
3248             cfg.cls += ' nav-' + this.type
3249         } else {
3250             if (this.type!=='nav') {
3251                 Roo.log('nav type must be nav/tabs/pills')
3252             }
3253             cfg.cls += ' navbar-nav'
3254         }
3255         
3256         if (this.parent().sidebar) {
3257             cfg = {
3258                 tag: 'ul',
3259                 cls: 'dashboard-menu sidebar-menu'
3260             }
3261             
3262             return cfg;
3263         }
3264         
3265         if (this.form === true) {
3266             cfg = {
3267                 tag: 'form',
3268                 cls: 'navbar-form'
3269             }
3270             
3271             if (this.align === 'right') {
3272                 cfg.cls += ' navbar-right';
3273             } else {
3274                 cfg.cls += ' navbar-left';
3275             }
3276         }
3277         
3278         if (this.align === 'right') {
3279             cfg.cls += ' navbar-right';
3280         }
3281         
3282         if (this.inverse) {
3283             cfg.cls += ' navbar-inverse';
3284             
3285         }
3286         
3287         
3288         return cfg;
3289     },
3290     /**
3291     * sets the active Navigation item
3292     * @param {Roo.bootstrap.NavItem} the new current navitem
3293     */
3294     setActiveItem : function(item)
3295     {
3296         var prev = false;
3297         Roo.each(this.navItems, function(v){
3298             if (v == item) {
3299                 return ;
3300             }
3301             if (v.isActive()) {
3302                 v.setActive(false, true);
3303                 prev = v;
3304                 
3305             }
3306             
3307         });
3308
3309         item.setActive(true, true);
3310         this.fireEvent('changed', this, item, prev);
3311         
3312         
3313     },
3314     /**
3315     * gets the active Navigation item
3316     * @return {Roo.bootstrap.NavItem} the current navitem
3317     */
3318     getActive : function()
3319     {
3320         
3321         var prev = false;
3322         Roo.each(this.navItems, function(v){
3323             
3324             if (v.isActive()) {
3325                 prev = v;
3326                 
3327             }
3328             
3329         });
3330         return prev;
3331     },
3332     
3333     indexOfNav : function()
3334     {
3335         
3336         var prev = false;
3337         Roo.each(this.navItems, function(v,i){
3338             
3339             if (v.isActive()) {
3340                 prev = i;
3341                 
3342             }
3343             
3344         });
3345         return prev;
3346     },
3347     /**
3348     * adds a Navigation item
3349     * @param {Roo.bootstrap.NavItem} the navitem to add
3350     */
3351     addItem : function(cfg)
3352     {
3353         var cn = new Roo.bootstrap.NavItem(cfg);
3354         this.register(cn);
3355         cn.parentId = this.id;
3356         cn.onRender(this.el, null);
3357         return cn;
3358     },
3359     /**
3360     * register a Navigation item
3361     * @param {Roo.bootstrap.NavItem} the navitem to add
3362     */
3363     register : function(item)
3364     {
3365         this.navItems.push( item);
3366         item.navId = this.navId;
3367     
3368     },
3369   
3370     
3371     getNavItem: function(tabId)
3372     {
3373         var ret = false;
3374         Roo.each(this.navItems, function(e) {
3375             if (e.tabId == tabId) {
3376                ret =  e;
3377                return false;
3378             }
3379             return true;
3380             
3381         });
3382         return ret;
3383     },
3384     
3385     setActiveNext : function()
3386     {
3387         var i = this.indexOfNav(this.getActive());
3388         if (i > this.navItems.length) {
3389             return;
3390         }
3391         this.setActiveItem(this.navItems[i+1]);
3392     },
3393     setActivePrev : function()
3394     {
3395         var i = this.indexOfNav(this.getActive());
3396         if (i  < 1) {
3397             return;
3398         }
3399         this.setActiveItem(this.navItems[i-1]);
3400     },
3401     clearWasActive : function(except) {
3402         Roo.each(this.navItems, function(e) {
3403             if (e.tabId != except.tabId && e.was_active) {
3404                e.was_active = false;
3405                return false;
3406             }
3407             return true;
3408             
3409         });
3410     },
3411     getWasActive : function ()
3412     {
3413         var r = false;
3414         Roo.each(this.navItems, function(e) {
3415             if (e.was_active) {
3416                r = e;
3417                return false;
3418             }
3419             return true;
3420             
3421         });
3422         return r;
3423     }
3424     
3425     
3426 });
3427
3428  
3429 Roo.apply(Roo.bootstrap.NavGroup, {
3430     
3431     groups: {},
3432      /**
3433     * register a Navigation Group
3434     * @param {Roo.bootstrap.NavGroup} the navgroup to add
3435     */
3436     register : function(navgrp)
3437     {
3438         this.groups[navgrp.navId] = navgrp;
3439         
3440     },
3441     /**
3442     * fetch a Navigation Group based on the navigation ID
3443     * @param {string} the navgroup to add
3444     * @returns {Roo.bootstrap.NavGroup} the navgroup 
3445     */
3446     get: function(navId) {
3447         if (typeof(this.groups[navId]) == 'undefined') {
3448             return false;
3449             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
3450         }
3451         return this.groups[navId] ;
3452     }
3453     
3454     
3455     
3456 });
3457
3458  /*
3459  * - LGPL
3460  *
3461  * row
3462  * 
3463  */
3464
3465 /**
3466  * @class Roo.bootstrap.NavItem
3467  * @extends Roo.bootstrap.Component
3468  * Bootstrap Navbar.NavItem class
3469  * @cfg {String} href  link to
3470  * @cfg {String} html content of button
3471  * @cfg {String} badge text inside badge
3472  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
3473  * @cfg {String} glyphicon name of glyphicon
3474  * @cfg {String} icon name of font awesome icon
3475  * @cfg {Boolean} active Is item active
3476  * @cfg {Boolean} disabled Is item disabled
3477  
3478  * @cfg {Boolean} preventDefault (true | false) default false
3479  * @cfg {String} tabId the tab that this item activates.
3480  * @cfg {String} tagtype (a|span) render as a href or span?
3481   
3482  * @constructor
3483  * Create a new Navbar Item
3484  * @param {Object} config The config object
3485  */
3486 Roo.bootstrap.NavItem = function(config){
3487     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
3488     this.addEvents({
3489         // raw events
3490         /**
3491          * @event click
3492          * The raw click event for the entire grid.
3493          * @param {Roo.EventObject} e
3494          */
3495         "click" : true,
3496          /**
3497             * @event changed
3498             * Fires when the active item active state changes
3499             * @param {Roo.bootstrap.NavItem} this
3500             * @param {boolean} state the new state
3501              
3502          */
3503         'changed': true
3504     });
3505    
3506 };
3507
3508 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
3509     
3510     href: false,
3511     html: '',
3512     badge: '',
3513     icon: false,
3514     glyphicon: false,
3515     active: false,
3516     preventDefault : false,
3517     tabId : false,
3518     tagtype : 'a',
3519     disabled : false,
3520     
3521     was_active : false,
3522     
3523     getAutoCreate : function(){
3524          
3525         var cfg = {
3526             tag: 'li',
3527             cls: 'nav-item'
3528             
3529         }
3530         if (this.active) {
3531             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
3532         }
3533         if (this.disabled) {
3534             cfg.cls += ' disabled';
3535         }
3536         
3537         if (this.href || this.html || this.glyphicon || this.icon) {
3538             cfg.cn = [
3539                 {
3540                     tag: this.tagtype,
3541                     href : this.href || "#",
3542                     html: this.html || ''
3543                 }
3544             ];
3545             
3546             if (this.icon) {
3547                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
3548             }
3549
3550             if(this.glyphicon) {
3551                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
3552             }
3553             
3554             if (this.menu) {
3555                 
3556                 cfg.cn[0].html += " <span class='caret'></span>";
3557              
3558             }
3559             
3560             if (this.badge !== '') {
3561                  
3562                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
3563             }
3564         }
3565         
3566         
3567         
3568         return cfg;
3569     },
3570     initEvents: function() {
3571        // Roo.log('init events?');
3572        // Roo.log(this.el.dom);
3573         if (typeof (this.menu) != 'undefined') {
3574             this.menu.parentType = this.xtype;
3575             this.menu.triggerEl = this.el;
3576             this.addxtype(Roo.apply({}, this.menu));
3577         }
3578
3579        
3580         this.el.select('a',true).on('click', this.onClick, this);
3581         // at this point parent should be available..
3582         this.parent().register(this);
3583     },
3584     
3585     onClick : function(e)
3586     {
3587          
3588         if(this.preventDefault){
3589             e.preventDefault();
3590         }
3591         if (this.disabled) {
3592             return;
3593         }
3594         
3595         var tg = Roo.bootstrap.TabGroup.get(this.navId);
3596         if (tg && tg.transition) {
3597             Roo.log("waiting for the transitionend");
3598             return;
3599         }
3600         
3601         Roo.log("fire event clicked");
3602         if(this.fireEvent('click', this, e) === false){
3603             return;
3604         };
3605         
3606         if (['tabs','pills'].indexOf(this.parent().type)!==-1) {
3607             if (typeof(this.parent().setActiveItem) !== 'undefined') {
3608                 this.parent().setActiveItem(this);
3609             }
3610         } 
3611     },
3612     
3613     isActive: function () {
3614         return this.active
3615     },
3616     setActive : function(state, fire, is_was_active)
3617     {
3618         if (this.active && !state & this.navId) {
3619             this.was_active = true;
3620             var nv = Roo.bootstrap.NavGroup.get(this.navId);
3621             if (nv) {
3622                 nv.clearWasActive(this);
3623             }
3624             
3625         }
3626         this.active = state;
3627         
3628         if (!state ) {
3629             this.el.removeClass('active');
3630         } else if (!this.el.hasClass('active')) {
3631             this.el.addClass('active');
3632         }
3633         if (fire) {
3634             this.fireEvent('changed', this, state);
3635         }
3636         
3637         // show a panel if it's registered and related..
3638         
3639         if (!this.navId || !this.tabId || !state || is_was_active) {
3640             return;
3641         }
3642         
3643         var tg = Roo.bootstrap.TabGroup.get(this.navId);
3644         if (!tg) {
3645             return;
3646         }
3647         var pan = tg.getPanelByName(this.tabId);
3648         if (!pan) {
3649             return;
3650         }
3651         // if we can not flip to new panel - go back to old nav highlight..
3652         if (false == tg.showPanel(pan)) {
3653             var nv = Roo.bootstrap.NavGroup.get(this.navId);
3654             if (nv) {
3655                 var onav = nv.getWasActive();
3656                 if (onav) {
3657                     onav.setActive(true, false, true);
3658                 }
3659             }
3660             
3661         }
3662         
3663         
3664         
3665     },
3666      // this should not be here...
3667     setDisabled : function(state)
3668     {
3669         this.disabled = state;
3670         if (!state ) {
3671             this.el.removeClass('disabled');
3672         } else if (!this.el.hasClass('disabled')) {
3673             this.el.addClass('disabled');
3674         }
3675         
3676     }
3677 });
3678  
3679
3680  /*
3681  * - LGPL
3682  *
3683  * sidebar item
3684  *
3685  *  li
3686  *    <span> icon </span>
3687  *    <span> text </span>
3688  *    <span>badge </span>
3689  */
3690
3691 /**
3692  * @class Roo.bootstrap.NavSidebarItem
3693  * @extends Roo.bootstrap.NavItem
3694  * Bootstrap Navbar.NavSidebarItem class
3695  * @constructor
3696  * Create a new Navbar Button
3697  * @param {Object} config The config object
3698  */
3699 Roo.bootstrap.NavSidebarItem = function(config){
3700     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
3701     this.addEvents({
3702         // raw events
3703         /**
3704          * @event click
3705          * The raw click event for the entire grid.
3706          * @param {Roo.EventObject} e
3707          */
3708         "click" : true,
3709          /**
3710             * @event changed
3711             * Fires when the active item active state changes
3712             * @param {Roo.bootstrap.NavSidebarItem} this
3713             * @param {boolean} state the new state
3714              
3715          */
3716         'changed': true
3717     });
3718    
3719 };
3720
3721 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
3722     
3723     
3724     getAutoCreate : function(){
3725         
3726         
3727         var a = {
3728                 tag: 'a',
3729                 href : this.href || '#',
3730                 cls: '',
3731                 html : '',
3732                 cn : []
3733         };
3734         var cfg = {
3735             tag: 'li',
3736             cls: '',
3737             cn: [ a ]
3738         }
3739         var span = {
3740             tag: 'span',
3741             html : this.html || ''
3742         }
3743         
3744         
3745         if (this.active) {
3746             cfg.cls += ' active';
3747         }
3748         
3749         // left icon..
3750         if (this.glyphicon || this.icon) {
3751             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
3752             a.cn.push({ tag : 'i', cls : c }) ;
3753         }
3754         // html..
3755         a.cn.push(span);
3756         // then badge..
3757         if (this.badge !== '') {
3758             a.cn.push({ tag: 'span',  cls : 'badge pull-right ' + (this.badgecls || ''), html: this.badge }); 
3759         }
3760         // fi
3761         if (this.menu) {
3762             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
3763             a.cls += 'dropdown-toggle treeview' ;
3764             
3765         }
3766         
3767         
3768         
3769         return cfg;
3770          
3771            
3772     }
3773    
3774      
3775  
3776 });
3777  
3778
3779  /*
3780  * - LGPL
3781  *
3782  * row
3783  * 
3784  */
3785
3786 /**
3787  * @class Roo.bootstrap.Row
3788  * @extends Roo.bootstrap.Component
3789  * Bootstrap Row class (contains columns...)
3790  * 
3791  * @constructor
3792  * Create a new Row
3793  * @param {Object} config The config object
3794  */
3795
3796 Roo.bootstrap.Row = function(config){
3797     Roo.bootstrap.Row.superclass.constructor.call(this, config);
3798 };
3799
3800 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
3801     
3802     getAutoCreate : function(){
3803        return {
3804             cls: 'row clearfix'
3805        };
3806     }
3807     
3808     
3809 });
3810
3811  
3812
3813  /*
3814  * - LGPL
3815  *
3816  * element
3817  * 
3818  */
3819
3820 /**
3821  * @class Roo.bootstrap.Element
3822  * @extends Roo.bootstrap.Component
3823  * Bootstrap Element class
3824  * @cfg {String} html contents of the element
3825  * @cfg {String} tag tag of the element
3826  * @cfg {String} cls class of the element
3827  * 
3828  * @constructor
3829  * Create a new Element
3830  * @param {Object} config The config object
3831  */
3832
3833 Roo.bootstrap.Element = function(config){
3834     Roo.bootstrap.Element.superclass.constructor.call(this, config);
3835 };
3836
3837 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
3838     
3839     tag: 'div',
3840     cls: '',
3841     html: '',
3842      
3843     
3844     getAutoCreate : function(){
3845         
3846         var cfg = {
3847             tag: this.tag,
3848             cls: this.cls,
3849             html: this.html
3850         }
3851         
3852         
3853         
3854         return cfg;
3855     }
3856    
3857 });
3858
3859  
3860
3861  /*
3862  * - LGPL
3863  *
3864  * pagination
3865  * 
3866  */
3867
3868 /**
3869  * @class Roo.bootstrap.Pagination
3870  * @extends Roo.bootstrap.Component
3871  * Bootstrap Pagination class
3872  * @cfg {String} size xs | sm | md | lg
3873  * @cfg {Boolean} inverse false | true
3874  * 
3875  * @constructor
3876  * Create a new Pagination
3877  * @param {Object} config The config object
3878  */
3879
3880 Roo.bootstrap.Pagination = function(config){
3881     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
3882 };
3883
3884 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
3885     
3886     cls: false,
3887     size: false,
3888     inverse: false,
3889     
3890     getAutoCreate : function(){
3891         var cfg = {
3892             tag: 'ul',
3893                 cls: 'pagination'
3894         };
3895         if (this.inverse) {
3896             cfg.cls += ' inverse';
3897         }
3898         if (this.html) {
3899             cfg.html=this.html;
3900         }
3901         if (this.cls) {
3902             cfg.cls += " " + this.cls;
3903         }
3904         return cfg;
3905     }
3906    
3907 });
3908
3909  
3910
3911  /*
3912  * - LGPL
3913  *
3914  * Pagination item
3915  * 
3916  */
3917
3918
3919 /**
3920  * @class Roo.bootstrap.PaginationItem
3921  * @extends Roo.bootstrap.Component
3922  * Bootstrap PaginationItem class
3923  * @cfg {String} html text
3924  * @cfg {String} href the link
3925  * @cfg {Boolean} preventDefault (true | false) default true
3926  * @cfg {Boolean} active (true | false) default false
3927  * 
3928  * 
3929  * @constructor
3930  * Create a new PaginationItem
3931  * @param {Object} config The config object
3932  */
3933
3934
3935 Roo.bootstrap.PaginationItem = function(config){
3936     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
3937     this.addEvents({
3938         // raw events
3939         /**
3940          * @event click
3941          * The raw click event for the entire grid.
3942          * @param {Roo.EventObject} e
3943          */
3944         "click" : true
3945     });
3946 };
3947
3948 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
3949     
3950     href : false,
3951     html : false,
3952     preventDefault: true,
3953     active : false,
3954     cls : false,
3955     
3956     getAutoCreate : function(){
3957         var cfg= {
3958             tag: 'li',
3959             cn: [
3960                 {
3961                     tag : 'a',
3962                     href : this.href ? this.href : '#',
3963                     html : this.html ? this.html : ''
3964                 }
3965             ]
3966         };
3967         
3968         if(this.cls){
3969             cfg.cls = this.cls;
3970         }
3971         
3972         if(this.active){
3973             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
3974         }
3975         
3976         return cfg;
3977     },
3978     
3979     initEvents: function() {
3980         
3981         this.el.on('click', this.onClick, this);
3982         
3983     },
3984     onClick : function(e)
3985     {
3986         Roo.log('PaginationItem on click ');
3987         if(this.preventDefault){
3988             e.preventDefault();
3989         }
3990         
3991         this.fireEvent('click', this, e);
3992     }
3993    
3994 });
3995
3996  
3997
3998  /*
3999  * - LGPL
4000  *
4001  * slider
4002  * 
4003  */
4004
4005
4006 /**
4007  * @class Roo.bootstrap.Slider
4008  * @extends Roo.bootstrap.Component
4009  * Bootstrap Slider class
4010  *    
4011  * @constructor
4012  * Create a new Slider
4013  * @param {Object} config The config object
4014  */
4015
4016 Roo.bootstrap.Slider = function(config){
4017     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4018 };
4019
4020 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
4021     
4022     getAutoCreate : function(){
4023         
4024         var cfg = {
4025             tag: 'div',
4026             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4027             cn: [
4028                 {
4029                     tag: 'a',
4030                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
4031                 }
4032             ]
4033         }
4034         
4035         return cfg;
4036     }
4037    
4038 });
4039
4040  /*
4041  * Based on:
4042  * Ext JS Library 1.1.1
4043  * Copyright(c) 2006-2007, Ext JS, LLC.
4044  *
4045  * Originally Released Under LGPL - original licence link has changed is not relivant.
4046  *
4047  * Fork - LGPL
4048  * <script type="text/javascript">
4049  */
4050  
4051
4052 /**
4053  * @class Roo.grid.ColumnModel
4054  * @extends Roo.util.Observable
4055  * This is the default implementation of a ColumnModel used by the Grid. It defines
4056  * the columns in the grid.
4057  * <br>Usage:<br>
4058  <pre><code>
4059  var colModel = new Roo.grid.ColumnModel([
4060         {header: "Ticker", width: 60, sortable: true, locked: true},
4061         {header: "Company Name", width: 150, sortable: true},
4062         {header: "Market Cap.", width: 100, sortable: true},
4063         {header: "$ Sales", width: 100, sortable: true, renderer: money},
4064         {header: "Employees", width: 100, sortable: true, resizable: false}
4065  ]);
4066  </code></pre>
4067  * <p>
4068  
4069  * The config options listed for this class are options which may appear in each
4070  * individual column definition.
4071  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4072  * @constructor
4073  * @param {Object} config An Array of column config objects. See this class's
4074  * config objects for details.
4075 */
4076 Roo.grid.ColumnModel = function(config){
4077         /**
4078      * The config passed into the constructor
4079      */
4080     this.config = config;
4081     this.lookup = {};
4082
4083     // if no id, create one
4084     // if the column does not have a dataIndex mapping,
4085     // map it to the order it is in the config
4086     for(var i = 0, len = config.length; i < len; i++){
4087         var c = config[i];
4088         if(typeof c.dataIndex == "undefined"){
4089             c.dataIndex = i;
4090         }
4091         if(typeof c.renderer == "string"){
4092             c.renderer = Roo.util.Format[c.renderer];
4093         }
4094         if(typeof c.id == "undefined"){
4095             c.id = Roo.id();
4096         }
4097         if(c.editor && c.editor.xtype){
4098             c.editor  = Roo.factory(c.editor, Roo.grid);
4099         }
4100         if(c.editor && c.editor.isFormField){
4101             c.editor = new Roo.grid.GridEditor(c.editor);
4102         }
4103         this.lookup[c.id] = c;
4104     }
4105
4106     /**
4107      * The width of columns which have no width specified (defaults to 100)
4108      * @type Number
4109      */
4110     this.defaultWidth = 100;
4111
4112     /**
4113      * Default sortable of columns which have no sortable specified (defaults to false)
4114      * @type Boolean
4115      */
4116     this.defaultSortable = false;
4117
4118     this.addEvents({
4119         /**
4120              * @event widthchange
4121              * Fires when the width of a column changes.
4122              * @param {ColumnModel} this
4123              * @param {Number} columnIndex The column index
4124              * @param {Number} newWidth The new width
4125              */
4126             "widthchange": true,
4127         /**
4128              * @event headerchange
4129              * Fires when the text of a header changes.
4130              * @param {ColumnModel} this
4131              * @param {Number} columnIndex The column index
4132              * @param {Number} newText The new header text
4133              */
4134             "headerchange": true,
4135         /**
4136              * @event hiddenchange
4137              * Fires when a column is hidden or "unhidden".
4138              * @param {ColumnModel} this
4139              * @param {Number} columnIndex The column index
4140              * @param {Boolean} hidden true if hidden, false otherwise
4141              */
4142             "hiddenchange": true,
4143             /**
4144          * @event columnmoved
4145          * Fires when a column is moved.
4146          * @param {ColumnModel} this
4147          * @param {Number} oldIndex
4148          * @param {Number} newIndex
4149          */
4150         "columnmoved" : true,
4151         /**
4152          * @event columlockchange
4153          * Fires when a column's locked state is changed
4154          * @param {ColumnModel} this
4155          * @param {Number} colIndex
4156          * @param {Boolean} locked true if locked
4157          */
4158         "columnlockchange" : true
4159     });
4160     Roo.grid.ColumnModel.superclass.constructor.call(this);
4161 };
4162 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4163     /**
4164      * @cfg {String} header The header text to display in the Grid view.
4165      */
4166     /**
4167      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4168      * {@link Roo.data.Record} definition from which to draw the column's value. If not
4169      * specified, the column's index is used as an index into the Record's data Array.
4170      */
4171     /**
4172      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4173      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4174      */
4175     /**
4176      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4177      * Defaults to the value of the {@link #defaultSortable} property.
4178      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4179      */
4180     /**
4181      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
4182      */
4183     /**
4184      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
4185      */
4186     /**
4187      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4188      */
4189     /**
4190      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4191      */
4192     /**
4193      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4194      * given the cell's data value. See {@link #setRenderer}. If not specified, the
4195      * default renderer uses the raw data value. If an object is returned (bootstrap only)
4196      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4197      */
4198        /**
4199      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
4200      */
4201     /**
4202      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
4203      */
4204
4205     /**
4206      * Returns the id of the column at the specified index.
4207      * @param {Number} index The column index
4208      * @return {String} the id
4209      */
4210     getColumnId : function(index){
4211         return this.config[index].id;
4212     },
4213
4214     /**
4215      * Returns the column for a specified id.
4216      * @param {String} id The column id
4217      * @return {Object} the column
4218      */
4219     getColumnById : function(id){
4220         return this.lookup[id];
4221     },
4222
4223     
4224     /**
4225      * Returns the column for a specified dataIndex.
4226      * @param {String} dataIndex The column dataIndex
4227      * @return {Object|Boolean} the column or false if not found
4228      */
4229     getColumnByDataIndex: function(dataIndex){
4230         var index = this.findColumnIndex(dataIndex);
4231         return index > -1 ? this.config[index] : false;
4232     },
4233     
4234     /**
4235      * Returns the index for a specified column id.
4236      * @param {String} id The column id
4237      * @return {Number} the index, or -1 if not found
4238      */
4239     getIndexById : function(id){
4240         for(var i = 0, len = this.config.length; i < len; i++){
4241             if(this.config[i].id == id){
4242                 return i;
4243             }
4244         }
4245         return -1;
4246     },
4247     
4248     /**
4249      * Returns the index for a specified column dataIndex.
4250      * @param {String} dataIndex The column dataIndex
4251      * @return {Number} the index, or -1 if not found
4252      */
4253     
4254     findColumnIndex : function(dataIndex){
4255         for(var i = 0, len = this.config.length; i < len; i++){
4256             if(this.config[i].dataIndex == dataIndex){
4257                 return i;
4258             }
4259         }
4260         return -1;
4261     },
4262     
4263     
4264     moveColumn : function(oldIndex, newIndex){
4265         var c = this.config[oldIndex];
4266         this.config.splice(oldIndex, 1);
4267         this.config.splice(newIndex, 0, c);
4268         this.dataMap = null;
4269         this.fireEvent("columnmoved", this, oldIndex, newIndex);
4270     },
4271
4272     isLocked : function(colIndex){
4273         return this.config[colIndex].locked === true;
4274     },
4275
4276     setLocked : function(colIndex, value, suppressEvent){
4277         if(this.isLocked(colIndex) == value){
4278             return;
4279         }
4280         this.config[colIndex].locked = value;
4281         if(!suppressEvent){
4282             this.fireEvent("columnlockchange", this, colIndex, value);
4283         }
4284     },
4285
4286     getTotalLockedWidth : function(){
4287         var totalWidth = 0;
4288         for(var i = 0; i < this.config.length; i++){
4289             if(this.isLocked(i) && !this.isHidden(i)){
4290                 this.totalWidth += this.getColumnWidth(i);
4291             }
4292         }
4293         return totalWidth;
4294     },
4295
4296     getLockedCount : function(){
4297         for(var i = 0, len = this.config.length; i < len; i++){
4298             if(!this.isLocked(i)){
4299                 return i;
4300             }
4301         }
4302     },
4303
4304     /**
4305      * Returns the number of columns.
4306      * @return {Number}
4307      */
4308     getColumnCount : function(visibleOnly){
4309         if(visibleOnly === true){
4310             var c = 0;
4311             for(var i = 0, len = this.config.length; i < len; i++){
4312                 if(!this.isHidden(i)){
4313                     c++;
4314                 }
4315             }
4316             return c;
4317         }
4318         return this.config.length;
4319     },
4320
4321     /**
4322      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
4323      * @param {Function} fn
4324      * @param {Object} scope (optional)
4325      * @return {Array} result
4326      */
4327     getColumnsBy : function(fn, scope){
4328         var r = [];
4329         for(var i = 0, len = this.config.length; i < len; i++){
4330             var c = this.config[i];
4331             if(fn.call(scope||this, c, i) === true){
4332                 r[r.length] = c;
4333             }
4334         }
4335         return r;
4336     },
4337
4338     /**
4339      * Returns true if the specified column is sortable.
4340      * @param {Number} col The column index
4341      * @return {Boolean}
4342      */
4343     isSortable : function(col){
4344         if(typeof this.config[col].sortable == "undefined"){
4345             return this.defaultSortable;
4346         }
4347         return this.config[col].sortable;
4348     },
4349
4350     /**
4351      * Returns the rendering (formatting) function defined for the column.
4352      * @param {Number} col The column index.
4353      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
4354      */
4355     getRenderer : function(col){
4356         if(!this.config[col].renderer){
4357             return Roo.grid.ColumnModel.defaultRenderer;
4358         }
4359         return this.config[col].renderer;
4360     },
4361
4362     /**
4363      * Sets the rendering (formatting) function for a column.
4364      * @param {Number} col The column index
4365      * @param {Function} fn The function to use to process the cell's raw data
4366      * to return HTML markup for the grid view. The render function is called with
4367      * the following parameters:<ul>
4368      * <li>Data value.</li>
4369      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
4370      * <li>css A CSS style string to apply to the table cell.</li>
4371      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
4372      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
4373      * <li>Row index</li>
4374      * <li>Column index</li>
4375      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
4376      */
4377     setRenderer : function(col, fn){
4378         this.config[col].renderer = fn;
4379     },
4380
4381     /**
4382      * Returns the width for the specified column.
4383      * @param {Number} col The column index
4384      * @return {Number}
4385      */
4386     getColumnWidth : function(col){
4387         return this.config[col].width * 1 || this.defaultWidth;
4388     },
4389
4390     /**
4391      * Sets the width for a column.
4392      * @param {Number} col The column index
4393      * @param {Number} width The new width
4394      */
4395     setColumnWidth : function(col, width, suppressEvent){
4396         this.config[col].width = width;
4397         this.totalWidth = null;
4398         if(!suppressEvent){
4399              this.fireEvent("widthchange", this, col, width);
4400         }
4401     },
4402
4403     /**
4404      * Returns the total width of all columns.
4405      * @param {Boolean} includeHidden True to include hidden column widths
4406      * @return {Number}
4407      */
4408     getTotalWidth : function(includeHidden){
4409         if(!this.totalWidth){
4410             this.totalWidth = 0;
4411             for(var i = 0, len = this.config.length; i < len; i++){
4412                 if(includeHidden || !this.isHidden(i)){
4413                     this.totalWidth += this.getColumnWidth(i);
4414                 }
4415             }
4416         }
4417         return this.totalWidth;
4418     },
4419
4420     /**
4421      * Returns the header for the specified column.
4422      * @param {Number} col The column index
4423      * @return {String}
4424      */
4425     getColumnHeader : function(col){
4426         return this.config[col].header;
4427     },
4428
4429     /**
4430      * Sets the header for a column.
4431      * @param {Number} col The column index
4432      * @param {String} header The new header
4433      */
4434     setColumnHeader : function(col, header){
4435         this.config[col].header = header;
4436         this.fireEvent("headerchange", this, col, header);
4437     },
4438
4439     /**
4440      * Returns the tooltip for the specified column.
4441      * @param {Number} col The column index
4442      * @return {String}
4443      */
4444     getColumnTooltip : function(col){
4445             return this.config[col].tooltip;
4446     },
4447     /**
4448      * Sets the tooltip for a column.
4449      * @param {Number} col The column index
4450      * @param {String} tooltip The new tooltip
4451      */
4452     setColumnTooltip : function(col, tooltip){
4453             this.config[col].tooltip = tooltip;
4454     },
4455
4456     /**
4457      * Returns the dataIndex for the specified column.
4458      * @param {Number} col The column index
4459      * @return {Number}
4460      */
4461     getDataIndex : function(col){
4462         return this.config[col].dataIndex;
4463     },
4464
4465     /**
4466      * Sets the dataIndex for a column.
4467      * @param {Number} col The column index
4468      * @param {Number} dataIndex The new dataIndex
4469      */
4470     setDataIndex : function(col, dataIndex){
4471         this.config[col].dataIndex = dataIndex;
4472     },
4473
4474     
4475     
4476     /**
4477      * Returns true if the cell is editable.
4478      * @param {Number} colIndex The column index
4479      * @param {Number} rowIndex The row index
4480      * @return {Boolean}
4481      */
4482     isCellEditable : function(colIndex, rowIndex){
4483         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
4484     },
4485
4486     /**
4487      * Returns the editor defined for the cell/column.
4488      * return false or null to disable editing.
4489      * @param {Number} colIndex The column index
4490      * @param {Number} rowIndex The row index
4491      * @return {Object}
4492      */
4493     getCellEditor : function(colIndex, rowIndex){
4494         return this.config[colIndex].editor;
4495     },
4496
4497     /**
4498      * Sets if a column is editable.
4499      * @param {Number} col The column index
4500      * @param {Boolean} editable True if the column is editable
4501      */
4502     setEditable : function(col, editable){
4503         this.config[col].editable = editable;
4504     },
4505
4506
4507     /**
4508      * Returns true if the column is hidden.
4509      * @param {Number} colIndex The column index
4510      * @return {Boolean}
4511      */
4512     isHidden : function(colIndex){
4513         return this.config[colIndex].hidden;
4514     },
4515
4516
4517     /**
4518      * Returns true if the column width cannot be changed
4519      */
4520     isFixed : function(colIndex){
4521         return this.config[colIndex].fixed;
4522     },
4523
4524     /**
4525      * Returns true if the column can be resized
4526      * @return {Boolean}
4527      */
4528     isResizable : function(colIndex){
4529         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
4530     },
4531     /**
4532      * Sets if a column is hidden.
4533      * @param {Number} colIndex The column index
4534      * @param {Boolean} hidden True if the column is hidden
4535      */
4536     setHidden : function(colIndex, hidden){
4537         this.config[colIndex].hidden = hidden;
4538         this.totalWidth = null;
4539         this.fireEvent("hiddenchange", this, colIndex, hidden);
4540     },
4541
4542     /**
4543      * Sets the editor for a column.
4544      * @param {Number} col The column index
4545      * @param {Object} editor The editor object
4546      */
4547     setEditor : function(col, editor){
4548         this.config[col].editor = editor;
4549     }
4550 });
4551
4552 Roo.grid.ColumnModel.defaultRenderer = function(value){
4553         if(typeof value == "string" && value.length < 1){
4554             return "&#160;";
4555         }
4556         return value;
4557 };
4558
4559 // Alias for backwards compatibility
4560 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
4561 /*
4562  * Based on:
4563  * Ext JS Library 1.1.1
4564  * Copyright(c) 2006-2007, Ext JS, LLC.
4565  *
4566  * Originally Released Under LGPL - original licence link has changed is not relivant.
4567  *
4568  * Fork - LGPL
4569  * <script type="text/javascript">
4570  */
4571  
4572 /**
4573  * @class Roo.LoadMask
4574  * A simple utility class for generically masking elements while loading data.  If the element being masked has
4575  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
4576  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
4577  * element's UpdateManager load indicator and will be destroyed after the initial load.
4578  * @constructor
4579  * Create a new LoadMask
4580  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
4581  * @param {Object} config The config object
4582  */
4583 Roo.LoadMask = function(el, config){
4584     this.el = Roo.get(el);
4585     Roo.apply(this, config);
4586     if(this.store){
4587         this.store.on('beforeload', this.onBeforeLoad, this);
4588         this.store.on('load', this.onLoad, this);
4589         this.store.on('loadexception', this.onLoadException, this);
4590         this.removeMask = false;
4591     }else{
4592         var um = this.el.getUpdateManager();
4593         um.showLoadIndicator = false; // disable the default indicator
4594         um.on('beforeupdate', this.onBeforeLoad, this);
4595         um.on('update', this.onLoad, this);
4596         um.on('failure', this.onLoad, this);
4597         this.removeMask = true;
4598     }
4599 };
4600
4601 Roo.LoadMask.prototype = {
4602     /**
4603      * @cfg {Boolean} removeMask
4604      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
4605      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
4606      */
4607     /**
4608      * @cfg {String} msg
4609      * The text to display in a centered loading message box (defaults to 'Loading...')
4610      */
4611     msg : 'Loading...',
4612     /**
4613      * @cfg {String} msgCls
4614      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
4615      */
4616     msgCls : 'x-mask-loading',
4617
4618     /**
4619      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
4620      * @type Boolean
4621      */
4622     disabled: false,
4623
4624     /**
4625      * Disables the mask to prevent it from being displayed
4626      */
4627     disable : function(){
4628        this.disabled = true;
4629     },
4630
4631     /**
4632      * Enables the mask so that it can be displayed
4633      */
4634     enable : function(){
4635         this.disabled = false;
4636     },
4637     
4638     onLoadException : function()
4639     {
4640         Roo.log(arguments);
4641         
4642         if (typeof(arguments[3]) != 'undefined') {
4643             Roo.MessageBox.alert("Error loading",arguments[3]);
4644         } 
4645         /*
4646         try {
4647             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
4648                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
4649             }   
4650         } catch(e) {
4651             
4652         }
4653         */
4654     
4655         
4656         
4657         this.el.unmask(this.removeMask);
4658     },
4659     // private
4660     onLoad : function()
4661     {
4662         this.el.unmask(this.removeMask);
4663     },
4664
4665     // private
4666     onBeforeLoad : function(){
4667         if(!this.disabled){
4668             this.el.mask(this.msg, this.msgCls);
4669         }
4670     },
4671
4672     // private
4673     destroy : function(){
4674         if(this.store){
4675             this.store.un('beforeload', this.onBeforeLoad, this);
4676             this.store.un('load', this.onLoad, this);
4677             this.store.un('loadexception', this.onLoadException, this);
4678         }else{
4679             var um = this.el.getUpdateManager();
4680             um.un('beforeupdate', this.onBeforeLoad, this);
4681             um.un('update', this.onLoad, this);
4682             um.un('failure', this.onLoad, this);
4683         }
4684     }
4685 };/*
4686  * - LGPL
4687  *
4688  * table
4689  * 
4690  */
4691
4692 /**
4693  * @class Roo.bootstrap.Table
4694  * @extends Roo.bootstrap.Component
4695  * Bootstrap Table class
4696  * @cfg {String} cls table class
4697  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
4698  * @cfg {String} bgcolor Specifies the background color for a table
4699  * @cfg {Number} border Specifies whether the table cells should have borders or not
4700  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
4701  * @cfg {Number} cellspacing Specifies the space between cells
4702  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
4703  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
4704  * @cfg {String} sortable Specifies that the table should be sortable
4705  * @cfg {String} summary Specifies a summary of the content of a table
4706  * @cfg {Number} width Specifies the width of a table
4707  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
4708  * 
4709  * @cfg {boolean} striped Should the rows be alternative striped
4710  * @cfg {boolean} bordered Add borders to the table
4711  * @cfg {boolean} hover Add hover highlighting
4712  * @cfg {boolean} condensed Format condensed
4713  * @cfg {boolean} responsive Format condensed
4714  * @cfg {Boolean} loadMask (true|false) default false
4715  * @cfg {Boolean} tfoot (true|false) generate tfoot, default true
4716  * @cfg {Boolean} thead (true|false) generate thead, default true
4717  * @cfg {Boolean} RowSelection (true|false) default false
4718  * @cfg {Boolean} CellSelection (true|false) default false
4719  *
4720  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
4721  
4722  * 
4723  * @constructor
4724  * Create a new Table
4725  * @param {Object} config The config object
4726  */
4727
4728 Roo.bootstrap.Table = function(config){
4729     Roo.bootstrap.Table.superclass.constructor.call(this, config);
4730     
4731     if (this.sm) {
4732         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
4733         this.sm = this.selModel;
4734         this.sm.xmodule = this.xmodule || false;
4735     }
4736     if (this.cm && typeof(this.cm.config) == 'undefined') {
4737         this.colModel = new Roo.grid.ColumnModel(this.cm);
4738         this.cm = this.colModel;
4739         this.cm.xmodule = this.xmodule || false;
4740     }
4741     if (this.store) {
4742         this.store= Roo.factory(this.store, Roo.data);
4743         this.ds = this.store;
4744         this.ds.xmodule = this.xmodule || false;
4745          
4746     }
4747     if (this.footer && this.store) {
4748         this.footer.dataSource = this.ds;
4749         this.footer = Roo.factory(this.footer);
4750     }
4751     
4752     /** @private */
4753     this.addEvents({
4754         /**
4755          * @event cellclick
4756          * Fires when a cell is clicked
4757          * @param {Roo.bootstrap.Table} this
4758          * @param {Roo.Element} el
4759          * @param {Number} rowIndex
4760          * @param {Number} columnIndex
4761          * @param {Roo.EventObject} e
4762          */
4763         "cellclick" : true,
4764         /**
4765          * @event celldblclick
4766          * Fires when a cell is double clicked
4767          * @param {Roo.bootstrap.Table} this
4768          * @param {Roo.Element} el
4769          * @param {Number} rowIndex
4770          * @param {Number} columnIndex
4771          * @param {Roo.EventObject} e
4772          */
4773         "celldblclick" : true,
4774         /**
4775          * @event rowclick
4776          * Fires when a row is clicked
4777          * @param {Roo.bootstrap.Table} this
4778          * @param {Roo.Element} el
4779          * @param {Number} rowIndex
4780          * @param {Roo.EventObject} e
4781          */
4782         "rowclick" : true,
4783         /**
4784          * @event rowdblclick
4785          * Fires when a row is double clicked
4786          * @param {Roo.bootstrap.Table} this
4787          * @param {Roo.Element} el
4788          * @param {Number} rowIndex
4789          * @param {Roo.EventObject} e
4790          */
4791         "rowdblclick" : true,
4792         /**
4793          * @event mouseover
4794          * Fires when a mouseover occur
4795          * @param {Roo.bootstrap.Table} this
4796          * @param {Roo.Element} el
4797          * @param {Number} rowIndex
4798          * @param {Number} columnIndex
4799          * @param {Roo.EventObject} e
4800          */
4801         "mouseover" : true,
4802         /**
4803          * @event mouseout
4804          * Fires when a mouseout occur
4805          * @param {Roo.bootstrap.Table} this
4806          * @param {Roo.Element} el
4807          * @param {Number} rowIndex
4808          * @param {Number} columnIndex
4809          * @param {Roo.EventObject} e
4810          */
4811         "mouseout" : true,
4812         /**
4813          * @event rowclass
4814          * Fires when a row is rendered, so you can change add a style to it.
4815          * @param {Roo.bootstrap.Table} this
4816          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
4817          */
4818         'rowclass' : true
4819         
4820     });
4821 };
4822
4823 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
4824     
4825     cls: false,
4826     align: false,
4827     bgcolor: false,
4828     border: false,
4829     cellpadding: false,
4830     cellspacing: false,
4831     frame: false,
4832     rules: false,
4833     sortable: false,
4834     summary: false,
4835     width: false,
4836     striped : false,
4837     bordered: false,
4838     hover:  false,
4839     condensed : false,
4840     responsive : false,
4841     sm : false,
4842     cm : false,
4843     store : false,
4844     loadMask : false,
4845     tfoot : true,
4846     thead : true,
4847     RowSelection : false,
4848     CellSelection : false,
4849     layout : false,
4850     
4851     // Roo.Element - the tbody
4852     mainBody: false, 
4853     
4854     getAutoCreate : function(){
4855         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
4856         
4857         cfg = {
4858             tag: 'table',
4859             cls : 'table',
4860             cn : []
4861         }
4862             
4863         if (this.striped) {
4864             cfg.cls += ' table-striped';
4865         }
4866         
4867         if (this.hover) {
4868             cfg.cls += ' table-hover';
4869         }
4870         if (this.bordered) {
4871             cfg.cls += ' table-bordered';
4872         }
4873         if (this.condensed) {
4874             cfg.cls += ' table-condensed';
4875         }
4876         if (this.responsive) {
4877             cfg.cls += ' table-responsive';
4878         }
4879         
4880         if (this.cls) {
4881             cfg.cls+=  ' ' +this.cls;
4882         }
4883         
4884         // this lot should be simplifed...
4885         
4886         if (this.align) {
4887             cfg.align=this.align;
4888         }
4889         if (this.bgcolor) {
4890             cfg.bgcolor=this.bgcolor;
4891         }
4892         if (this.border) {
4893             cfg.border=this.border;
4894         }
4895         if (this.cellpadding) {
4896             cfg.cellpadding=this.cellpadding;
4897         }
4898         if (this.cellspacing) {
4899             cfg.cellspacing=this.cellspacing;
4900         }
4901         if (this.frame) {
4902             cfg.frame=this.frame;
4903         }
4904         if (this.rules) {
4905             cfg.rules=this.rules;
4906         }
4907         if (this.sortable) {
4908             cfg.sortable=this.sortable;
4909         }
4910         if (this.summary) {
4911             cfg.summary=this.summary;
4912         }
4913         if (this.width) {
4914             cfg.width=this.width;
4915         }
4916         if (this.layout) {
4917             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
4918         }
4919         
4920         if(this.store || this.cm){
4921             if(this.thead){
4922                 cfg.cn.push(this.renderHeader());
4923             }
4924             
4925             cfg.cn.push(this.renderBody());
4926             
4927             if(this.tfoot){
4928                 cfg.cn.push(this.renderFooter());
4929             }
4930             
4931             cfg.cls+=  ' TableGrid';
4932         }
4933         
4934         return { cn : [ cfg ] };
4935     },
4936     
4937     initEvents : function()
4938     {   
4939         if(!this.store || !this.cm){
4940             return;
4941         }
4942         
4943         //Roo.log('initEvents with ds!!!!');
4944         
4945         this.mainBody = this.el.select('tbody', true).first();
4946         
4947         
4948         var _this = this;
4949         
4950         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
4951             e.on('click', _this.sort, _this);
4952         });
4953         
4954         this.el.on("click", this.onClick, this);
4955         this.el.on("dblclick", this.onDblClick, this);
4956         
4957         this.parent().el.setStyle('position', 'relative');
4958         if (this.footer) {
4959             this.footer.parentId = this.id;
4960             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
4961         }
4962         
4963         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
4964         
4965         this.store.on('load', this.onLoad, this);
4966         this.store.on('beforeload', this.onBeforeLoad, this);
4967         this.store.on('update', this.onUpdate, this);
4968         
4969     },
4970     
4971     onMouseover : function(e, el)
4972     {
4973         var cell = Roo.get(el);
4974         
4975         if(!cell){
4976             return;
4977         }
4978         
4979         if(e.getTarget().nodeName.toLowerCase() != 'td'){
4980             cell = cell.findParent('td', false, true);
4981         }
4982         
4983         var row = cell.findParent('tr', false, true);
4984         var cellIndex = cell.dom.cellIndex;
4985         var rowIndex = row.dom.rowIndex - 1; // start from 0
4986         
4987         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
4988         
4989     },
4990     
4991     onMouseout : function(e, el)
4992     {
4993         var cell = Roo.get(el);
4994         
4995         if(!cell){
4996             return;
4997         }
4998         
4999         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5000             cell = cell.findParent('td', false, true);
5001         }
5002         
5003         var row = cell.findParent('tr', false, true);
5004         var cellIndex = cell.dom.cellIndex;
5005         var rowIndex = row.dom.rowIndex - 1; // start from 0
5006         
5007         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5008         
5009     },
5010     
5011     onClick : function(e, el)
5012     {
5013         var cell = Roo.get(el);
5014         
5015         if(!cell || (!this.CellSelection && !this.RowSelection)){
5016             return;
5017         }
5018         
5019         
5020         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5021             cell = cell.findParent('td', false, true);
5022         }
5023         
5024         var row = cell.findParent('tr', false, true);
5025         var cellIndex = cell.dom.cellIndex;
5026         var rowIndex = row.dom.rowIndex - 1;
5027         
5028         if(this.CellSelection){
5029             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5030         }
5031         
5032         if(this.RowSelection){
5033             this.fireEvent('rowclick', this, row, rowIndex, e);
5034         }
5035         
5036         
5037     },
5038     
5039     onDblClick : function(e,el)
5040     {
5041         var cell = Roo.get(el);
5042         
5043         if(!cell || (!this.CellSelection && !this.RowSelection)){
5044             return;
5045         }
5046         
5047         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5048             cell = cell.findParent('td', false, true);
5049         }
5050         
5051         var row = cell.findParent('tr', false, true);
5052         var cellIndex = cell.dom.cellIndex;
5053         var rowIndex = row.dom.rowIndex - 1;
5054         
5055         if(this.CellSelection){
5056             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5057         }
5058         
5059         if(this.RowSelection){
5060             this.fireEvent('rowdblclick', this, row, rowIndex, e);
5061         }
5062     },
5063     
5064     sort : function(e,el)
5065     {
5066         var col = Roo.get(el)
5067         
5068         if(!col.hasClass('sortable')){
5069             return;
5070         }
5071         
5072         var sort = col.attr('sort');
5073         var dir = 'ASC';
5074         
5075         if(col.hasClass('glyphicon-arrow-up')){
5076             dir = 'DESC';
5077         }
5078         
5079         this.store.sortInfo = {field : sort, direction : dir};
5080         
5081         if (this.footer) {
5082             Roo.log("calling footer first");
5083             this.footer.onClick('first');
5084         } else {
5085         
5086             this.store.load({ params : { start : 0 } });
5087         }
5088     },
5089     
5090     renderHeader : function()
5091     {
5092         var header = {
5093             tag: 'thead',
5094             cn : []
5095         };
5096         
5097         var cm = this.cm;
5098         
5099         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5100             
5101             var config = cm.config[i];
5102                     
5103             var c = {
5104                 tag: 'th',
5105                 style : '',
5106                 html: cm.getColumnHeader(i)
5107             };
5108             
5109             if(typeof(config.hidden) != 'undefined' && config.hidden){
5110                 c.style += ' display:none;';
5111             }
5112             
5113             if(typeof(config.dataIndex) != 'undefined'){
5114                 c.sort = config.dataIndex;
5115             }
5116             
5117             if(typeof(config.sortable) != 'undefined' && config.sortable){
5118                 c.cls = 'sortable';
5119             }
5120             
5121             if(typeof(config.align) != 'undefined' && config.align.length){
5122                 c.style += ' text-align:' + config.align + ';';
5123             }
5124             
5125             if(typeof(config.width) != 'undefined'){
5126                 c.style += ' width:' + config.width + 'px;';
5127             }
5128             
5129             header.cn.push(c)
5130         }
5131         
5132         return header;
5133     },
5134     
5135     renderBody : function()
5136     {
5137         var body = {
5138             tag: 'tbody',
5139             cn : [
5140                 {
5141                     tag: 'tr',
5142                     cn : [
5143                         {
5144                             tag : 'td',
5145                             colspan :  this.cm.getColumnCount()
5146                         }
5147                     ]
5148                 }
5149             ]
5150         };
5151         
5152         return body;
5153     },
5154     
5155     renderFooter : function()
5156     {
5157         var footer = {
5158             tag: 'tfoot',
5159             cn : [
5160                 {
5161                     tag: 'tr',
5162                     cn : [
5163                         {
5164                             tag : 'td',
5165                             colspan :  this.cm.getColumnCount()
5166                         }
5167                     ]
5168                 }
5169             ]
5170         };
5171         
5172         return footer;
5173     },
5174     
5175     
5176     
5177     onLoad : function()
5178     {
5179         Roo.log('ds onload');
5180         this.clear();
5181         
5182         var _this = this;
5183         var cm = this.cm;
5184         var ds = this.store;
5185         
5186         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5187             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
5188             
5189             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
5190                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
5191             }
5192             
5193             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
5194                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
5195             }
5196         });
5197         
5198         var tbody =  this.mainBody;
5199               
5200         if(ds.getCount() > 0){
5201             ds.data.each(function(d,rowIndex){
5202                 var row =  this.renderRow(cm, ds, rowIndex);
5203                 
5204                 tbody.createChild(row);
5205                 
5206                 var _this = this;
5207                 
5208                 if(row.cellObjects.length){
5209                     Roo.each(row.cellObjects, function(r){
5210                         _this.renderCellObject(r);
5211                     })
5212                 }
5213                 
5214             }, this);
5215         }
5216         
5217         Roo.each(this.el.select('tbody td', true).elements, function(e){
5218             e.on('mouseover', _this.onMouseover, _this);
5219         });
5220         
5221         Roo.each(this.el.select('tbody td', true).elements, function(e){
5222             e.on('mouseout', _this.onMouseout, _this);
5223         });
5224
5225         //if(this.loadMask){
5226         //    this.maskEl.hide();
5227         //}
5228     },
5229     
5230     
5231     onUpdate : function(ds,record)
5232     {
5233         this.refreshRow(record);
5234     },
5235     onRemove : function(ds, record, index, isUpdate){
5236         if(isUpdate !== true){
5237             this.fireEvent("beforerowremoved", this, index, record);
5238         }
5239         var bt = this.mainBody.dom;
5240         if(bt.rows[index]){
5241             bt.removeChild(bt.rows[index]);
5242         }
5243         
5244         if(isUpdate !== true){
5245             //this.stripeRows(index);
5246             //this.syncRowHeights(index, index);
5247             //this.layout();
5248             this.fireEvent("rowremoved", this, index, record);
5249         }
5250     },
5251     
5252     
5253     refreshRow : function(record){
5254         var ds = this.store, index;
5255         if(typeof record == 'number'){
5256             index = record;
5257             record = ds.getAt(index);
5258         }else{
5259             index = ds.indexOf(record);
5260         }
5261         this.insertRow(ds, index, true);
5262         this.onRemove(ds, record, index+1, true);
5263         //this.syncRowHeights(index, index);
5264         //this.layout();
5265         this.fireEvent("rowupdated", this, index, record);
5266     },
5267     
5268     insertRow : function(dm, rowIndex, isUpdate){
5269         
5270         if(!isUpdate){
5271             this.fireEvent("beforerowsinserted", this, rowIndex);
5272         }
5273             //var s = this.getScrollState();
5274         var row = this.renderRow(this.cm, this.store, rowIndex);
5275         // insert before rowIndex..
5276         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
5277         
5278         var _this = this;
5279                 
5280         if(row.cellObjects.length){
5281             Roo.each(row.cellObjects, function(r){
5282                 _this.renderCellObject(r);
5283             })
5284         }
5285             
5286         if(!isUpdate){
5287             this.fireEvent("rowsinserted", this, rowIndex);
5288             //this.syncRowHeights(firstRow, lastRow);
5289             //this.stripeRows(firstRow);
5290             //this.layout();
5291         }
5292         
5293     },
5294     
5295     
5296     getRowDom : function(rowIndex)
5297     {
5298         // not sure if I need to check this.. but let's do it anyway..
5299         return (this.mainBody.dom.rows && (rowIndex-1) < this.mainBody.dom.rows.length ) ?
5300                 this.mainBody.dom.rows[rowIndex] : false
5301     },
5302     // returns the object tree for a tr..
5303   
5304     
5305     renderRow : function(cm, ds, rowIndex) {
5306         
5307         var d = ds.getAt(rowIndex);
5308         
5309         var row = {
5310             tag : 'tr',
5311             cn : []
5312         };
5313             
5314         var cellObjects = [];
5315         
5316         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5317             var config = cm.config[i];
5318             
5319             var renderer = cm.getRenderer(i);
5320             var value = '';
5321             var id = false;
5322             
5323             if(typeof(renderer) !== 'undefined'){
5324                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
5325             }
5326             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
5327             // and are rendered into the cells after the row is rendered - using the id for the element.
5328             
5329             if(typeof(value) === 'object'){
5330                 id = Roo.id();
5331                 cellObjects.push({
5332                     container : id,
5333                     cfg : value 
5334                 })
5335             }
5336             
5337             var rowcfg = {
5338                 record: d,
5339                 rowIndex : rowIndex,
5340                 colIndex : i,
5341                 rowClass : ''
5342             }
5343
5344             this.fireEvent('rowclass', this, rowcfg);
5345             
5346             var td = {
5347                 tag: 'td',
5348                 cls : rowcfg.rowClass,
5349                 style: '',
5350                 html: (typeof(value) === 'object') ? '' : value
5351             };
5352             
5353             if (id) {
5354                 td.id = id;
5355             }
5356             
5357             if(typeof(config.hidden) != 'undefined' && config.hidden){
5358                 td.style += ' display:none;';
5359             }
5360             
5361             if(typeof(config.align) != 'undefined' && config.align.length){
5362                 td.style += ' text-align:' + config.align + ';';
5363             }
5364             
5365             if(typeof(config.width) != 'undefined'){
5366                 td.style += ' width:' +  config.width + 'px;';
5367             }
5368              
5369             row.cn.push(td);
5370            
5371         }
5372         
5373         row.cellObjects = cellObjects;
5374         
5375         return row;
5376           
5377     },
5378     
5379     
5380     
5381     onBeforeLoad : function()
5382     {
5383         //Roo.log('ds onBeforeLoad');
5384         
5385         //this.clear();
5386         
5387         //if(this.loadMask){
5388         //    this.maskEl.show();
5389         //}
5390     },
5391     
5392     clear : function()
5393     {
5394         this.el.select('tbody', true).first().dom.innerHTML = '';
5395     },
5396     
5397     getSelectionModel : function(){
5398         if(!this.selModel){
5399             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
5400         }
5401         return this.selModel;
5402     },
5403     /*
5404      * Render the Roo.bootstrap object from renderder
5405      */
5406     renderCellObject : function(r)
5407     {
5408         var _this = this;
5409         
5410         var t = r.cfg.render(r.container);
5411         
5412         if(r.cfg.cn){
5413             Roo.each(r.cfg.cn, function(c){
5414                 var child = {
5415                     container: t.getChildContainer(),
5416                     cfg: c
5417                 }
5418                 _this.renderCellObject(child);
5419             })
5420         }
5421     }
5422    
5423 });
5424
5425  
5426
5427  /*
5428  * - LGPL
5429  *
5430  * table cell
5431  * 
5432  */
5433
5434 /**
5435  * @class Roo.bootstrap.TableCell
5436  * @extends Roo.bootstrap.Component
5437  * Bootstrap TableCell class
5438  * @cfg {String} html cell contain text
5439  * @cfg {String} cls cell class
5440  * @cfg {String} tag cell tag (td|th) default td
5441  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
5442  * @cfg {String} align Aligns the content in a cell
5443  * @cfg {String} axis Categorizes cells
5444  * @cfg {String} bgcolor Specifies the background color of a cell
5445  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5446  * @cfg {Number} colspan Specifies the number of columns a cell should span
5447  * @cfg {String} headers Specifies one or more header cells a cell is related to
5448  * @cfg {Number} height Sets the height of a cell
5449  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
5450  * @cfg {Number} rowspan Sets the number of rows a cell should span
5451  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
5452  * @cfg {String} valign Vertical aligns the content in a cell
5453  * @cfg {Number} width Specifies the width of a cell
5454  * 
5455  * @constructor
5456  * Create a new TableCell
5457  * @param {Object} config The config object
5458  */
5459
5460 Roo.bootstrap.TableCell = function(config){
5461     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
5462 };
5463
5464 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
5465     
5466     html: false,
5467     cls: false,
5468     tag: false,
5469     abbr: false,
5470     align: false,
5471     axis: false,
5472     bgcolor: false,
5473     charoff: false,
5474     colspan: false,
5475     headers: false,
5476     height: false,
5477     nowrap: false,
5478     rowspan: false,
5479     scope: false,
5480     valign: false,
5481     width: false,
5482     
5483     
5484     getAutoCreate : function(){
5485         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
5486         
5487         cfg = {
5488             tag: 'td'
5489         }
5490         
5491         if(this.tag){
5492             cfg.tag = this.tag;
5493         }
5494         
5495         if (this.html) {
5496             cfg.html=this.html
5497         }
5498         if (this.cls) {
5499             cfg.cls=this.cls
5500         }
5501         if (this.abbr) {
5502             cfg.abbr=this.abbr
5503         }
5504         if (this.align) {
5505             cfg.align=this.align
5506         }
5507         if (this.axis) {
5508             cfg.axis=this.axis
5509         }
5510         if (this.bgcolor) {
5511             cfg.bgcolor=this.bgcolor
5512         }
5513         if (this.charoff) {
5514             cfg.charoff=this.charoff
5515         }
5516         if (this.colspan) {
5517             cfg.colspan=this.colspan
5518         }
5519         if (this.headers) {
5520             cfg.headers=this.headers
5521         }
5522         if (this.height) {
5523             cfg.height=this.height
5524         }
5525         if (this.nowrap) {
5526             cfg.nowrap=this.nowrap
5527         }
5528         if (this.rowspan) {
5529             cfg.rowspan=this.rowspan
5530         }
5531         if (this.scope) {
5532             cfg.scope=this.scope
5533         }
5534         if (this.valign) {
5535             cfg.valign=this.valign
5536         }
5537         if (this.width) {
5538             cfg.width=this.width
5539         }
5540         
5541         
5542         return cfg;
5543     }
5544    
5545 });
5546
5547  
5548
5549  /*
5550  * - LGPL
5551  *
5552  * table row
5553  * 
5554  */
5555
5556 /**
5557  * @class Roo.bootstrap.TableRow
5558  * @extends Roo.bootstrap.Component
5559  * Bootstrap TableRow class
5560  * @cfg {String} cls row class
5561  * @cfg {String} align Aligns the content in a table row
5562  * @cfg {String} bgcolor Specifies a background color for a table row
5563  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5564  * @cfg {String} valign Vertical aligns the content in a table row
5565  * 
5566  * @constructor
5567  * Create a new TableRow
5568  * @param {Object} config The config object
5569  */
5570
5571 Roo.bootstrap.TableRow = function(config){
5572     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
5573 };
5574
5575 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
5576     
5577     cls: false,
5578     align: false,
5579     bgcolor: false,
5580     charoff: false,
5581     valign: false,
5582     
5583     getAutoCreate : function(){
5584         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
5585         
5586         cfg = {
5587             tag: 'tr'
5588         }
5589             
5590         if(this.cls){
5591             cfg.cls = this.cls;
5592         }
5593         if(this.align){
5594             cfg.align = this.align;
5595         }
5596         if(this.bgcolor){
5597             cfg.bgcolor = this.bgcolor;
5598         }
5599         if(this.charoff){
5600             cfg.charoff = this.charoff;
5601         }
5602         if(this.valign){
5603             cfg.valign = this.valign;
5604         }
5605         
5606         return cfg;
5607     }
5608    
5609 });
5610
5611  
5612
5613  /*
5614  * - LGPL
5615  *
5616  * table body
5617  * 
5618  */
5619
5620 /**
5621  * @class Roo.bootstrap.TableBody
5622  * @extends Roo.bootstrap.Component
5623  * Bootstrap TableBody class
5624  * @cfg {String} cls element class
5625  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
5626  * @cfg {String} align Aligns the content inside the element
5627  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
5628  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
5629  * 
5630  * @constructor
5631  * Create a new TableBody
5632  * @param {Object} config The config object
5633  */
5634
5635 Roo.bootstrap.TableBody = function(config){
5636     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
5637 };
5638
5639 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
5640     
5641     cls: false,
5642     tag: false,
5643     align: false,
5644     charoff: false,
5645     valign: false,
5646     
5647     getAutoCreate : function(){
5648         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
5649         
5650         cfg = {
5651             tag: 'tbody'
5652         }
5653             
5654         if (this.cls) {
5655             cfg.cls=this.cls
5656         }
5657         if(this.tag){
5658             cfg.tag = this.tag;
5659         }
5660         
5661         if(this.align){
5662             cfg.align = this.align;
5663         }
5664         if(this.charoff){
5665             cfg.charoff = this.charoff;
5666         }
5667         if(this.valign){
5668             cfg.valign = this.valign;
5669         }
5670         
5671         return cfg;
5672     }
5673     
5674     
5675 //    initEvents : function()
5676 //    {
5677 //        
5678 //        if(!this.store){
5679 //            return;
5680 //        }
5681 //        
5682 //        this.store = Roo.factory(this.store, Roo.data);
5683 //        this.store.on('load', this.onLoad, this);
5684 //        
5685 //        this.store.load();
5686 //        
5687 //    },
5688 //    
5689 //    onLoad: function () 
5690 //    {   
5691 //        this.fireEvent('load', this);
5692 //    }
5693 //    
5694 //   
5695 });
5696
5697  
5698
5699  /*
5700  * Based on:
5701  * Ext JS Library 1.1.1
5702  * Copyright(c) 2006-2007, Ext JS, LLC.
5703  *
5704  * Originally Released Under LGPL - original licence link has changed is not relivant.
5705  *
5706  * Fork - LGPL
5707  * <script type="text/javascript">
5708  */
5709
5710 // as we use this in bootstrap.
5711 Roo.namespace('Roo.form');
5712  /**
5713  * @class Roo.form.Action
5714  * Internal Class used to handle form actions
5715  * @constructor
5716  * @param {Roo.form.BasicForm} el The form element or its id
5717  * @param {Object} config Configuration options
5718  */
5719
5720  
5721  
5722 // define the action interface
5723 Roo.form.Action = function(form, options){
5724     this.form = form;
5725     this.options = options || {};
5726 };
5727 /**
5728  * Client Validation Failed
5729  * @const 
5730  */
5731 Roo.form.Action.CLIENT_INVALID = 'client';
5732 /**
5733  * Server Validation Failed
5734  * @const 
5735  */
5736 Roo.form.Action.SERVER_INVALID = 'server';
5737  /**
5738  * Connect to Server Failed
5739  * @const 
5740  */
5741 Roo.form.Action.CONNECT_FAILURE = 'connect';
5742 /**
5743  * Reading Data from Server Failed
5744  * @const 
5745  */
5746 Roo.form.Action.LOAD_FAILURE = 'load';
5747
5748 Roo.form.Action.prototype = {
5749     type : 'default',
5750     failureType : undefined,
5751     response : undefined,
5752     result : undefined,
5753
5754     // interface method
5755     run : function(options){
5756
5757     },
5758
5759     // interface method
5760     success : function(response){
5761
5762     },
5763
5764     // interface method
5765     handleResponse : function(response){
5766
5767     },
5768
5769     // default connection failure
5770     failure : function(response){
5771         
5772         this.response = response;
5773         this.failureType = Roo.form.Action.CONNECT_FAILURE;
5774         this.form.afterAction(this, false);
5775     },
5776
5777     processResponse : function(response){
5778         this.response = response;
5779         if(!response.responseText){
5780             return true;
5781         }
5782         this.result = this.handleResponse(response);
5783         return this.result;
5784     },
5785
5786     // utility functions used internally
5787     getUrl : function(appendParams){
5788         var url = this.options.url || this.form.url || this.form.el.dom.action;
5789         if(appendParams){
5790             var p = this.getParams();
5791             if(p){
5792                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
5793             }
5794         }
5795         return url;
5796     },
5797
5798     getMethod : function(){
5799         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
5800     },
5801
5802     getParams : function(){
5803         var bp = this.form.baseParams;
5804         var p = this.options.params;
5805         if(p){
5806             if(typeof p == "object"){
5807                 p = Roo.urlEncode(Roo.applyIf(p, bp));
5808             }else if(typeof p == 'string' && bp){
5809                 p += '&' + Roo.urlEncode(bp);
5810             }
5811         }else if(bp){
5812             p = Roo.urlEncode(bp);
5813         }
5814         return p;
5815     },
5816
5817     createCallback : function(){
5818         return {
5819             success: this.success,
5820             failure: this.failure,
5821             scope: this,
5822             timeout: (this.form.timeout*1000),
5823             upload: this.form.fileUpload ? this.success : undefined
5824         };
5825     }
5826 };
5827
5828 Roo.form.Action.Submit = function(form, options){
5829     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
5830 };
5831
5832 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
5833     type : 'submit',
5834
5835     haveProgress : false,
5836     uploadComplete : false,
5837     
5838     // uploadProgress indicator.
5839     uploadProgress : function()
5840     {
5841         if (!this.form.progressUrl) {
5842             return;
5843         }
5844         
5845         if (!this.haveProgress) {
5846             Roo.MessageBox.progress("Uploading", "Uploading");
5847         }
5848         if (this.uploadComplete) {
5849            Roo.MessageBox.hide();
5850            return;
5851         }
5852         
5853         this.haveProgress = true;
5854    
5855         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
5856         
5857         var c = new Roo.data.Connection();
5858         c.request({
5859             url : this.form.progressUrl,
5860             params: {
5861                 id : uid
5862             },
5863             method: 'GET',
5864             success : function(req){
5865                //console.log(data);
5866                 var rdata = false;
5867                 var edata;
5868                 try  {
5869                    rdata = Roo.decode(req.responseText)
5870                 } catch (e) {
5871                     Roo.log("Invalid data from server..");
5872                     Roo.log(edata);
5873                     return;
5874                 }
5875                 if (!rdata || !rdata.success) {
5876                     Roo.log(rdata);
5877                     Roo.MessageBox.alert(Roo.encode(rdata));
5878                     return;
5879                 }
5880                 var data = rdata.data;
5881                 
5882                 if (this.uploadComplete) {
5883                    Roo.MessageBox.hide();
5884                    return;
5885                 }
5886                    
5887                 if (data){
5888                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
5889                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
5890                     );
5891                 }
5892                 this.uploadProgress.defer(2000,this);
5893             },
5894        
5895             failure: function(data) {
5896                 Roo.log('progress url failed ');
5897                 Roo.log(data);
5898             },
5899             scope : this
5900         });
5901            
5902     },
5903     
5904     
5905     run : function()
5906     {
5907         // run get Values on the form, so it syncs any secondary forms.
5908         this.form.getValues();
5909         
5910         var o = this.options;
5911         var method = this.getMethod();
5912         var isPost = method == 'POST';
5913         if(o.clientValidation === false || this.form.isValid()){
5914             
5915             if (this.form.progressUrl) {
5916                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
5917                     (new Date() * 1) + '' + Math.random());
5918                     
5919             } 
5920             
5921             
5922             Roo.Ajax.request(Roo.apply(this.createCallback(), {
5923                 form:this.form.el.dom,
5924                 url:this.getUrl(!isPost),
5925                 method: method,
5926                 params:isPost ? this.getParams() : null,
5927                 isUpload: this.form.fileUpload
5928             }));
5929             
5930             this.uploadProgress();
5931
5932         }else if (o.clientValidation !== false){ // client validation failed
5933             this.failureType = Roo.form.Action.CLIENT_INVALID;
5934             this.form.afterAction(this, false);
5935         }
5936     },
5937
5938     success : function(response)
5939     {
5940         this.uploadComplete= true;
5941         if (this.haveProgress) {
5942             Roo.MessageBox.hide();
5943         }
5944         
5945         
5946         var result = this.processResponse(response);
5947         if(result === true || result.success){
5948             this.form.afterAction(this, true);
5949             return;
5950         }
5951         if(result.errors){
5952             this.form.markInvalid(result.errors);
5953             this.failureType = Roo.form.Action.SERVER_INVALID;
5954         }
5955         this.form.afterAction(this, false);
5956     },
5957     failure : function(response)
5958     {
5959         this.uploadComplete= true;
5960         if (this.haveProgress) {
5961             Roo.MessageBox.hide();
5962         }
5963         
5964         this.response = response;
5965         this.failureType = Roo.form.Action.CONNECT_FAILURE;
5966         this.form.afterAction(this, false);
5967     },
5968     
5969     handleResponse : function(response){
5970         if(this.form.errorReader){
5971             var rs = this.form.errorReader.read(response);
5972             var errors = [];
5973             if(rs.records){
5974                 for(var i = 0, len = rs.records.length; i < len; i++) {
5975                     var r = rs.records[i];
5976                     errors[i] = r.data;
5977                 }
5978             }
5979             if(errors.length < 1){
5980                 errors = null;
5981             }
5982             return {
5983                 success : rs.success,
5984                 errors : errors
5985             };
5986         }
5987         var ret = false;
5988         try {
5989             ret = Roo.decode(response.responseText);
5990         } catch (e) {
5991             ret = {
5992                 success: false,
5993                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
5994                 errors : []
5995             };
5996         }
5997         return ret;
5998         
5999     }
6000 });
6001
6002
6003 Roo.form.Action.Load = function(form, options){
6004     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6005     this.reader = this.form.reader;
6006 };
6007
6008 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6009     type : 'load',
6010
6011     run : function(){
6012         
6013         Roo.Ajax.request(Roo.apply(
6014                 this.createCallback(), {
6015                     method:this.getMethod(),
6016                     url:this.getUrl(false),
6017                     params:this.getParams()
6018         }));
6019     },
6020
6021     success : function(response){
6022         
6023         var result = this.processResponse(response);
6024         if(result === true || !result.success || !result.data){
6025             this.failureType = Roo.form.Action.LOAD_FAILURE;
6026             this.form.afterAction(this, false);
6027             return;
6028         }
6029         this.form.clearInvalid();
6030         this.form.setValues(result.data);
6031         this.form.afterAction(this, true);
6032     },
6033
6034     handleResponse : function(response){
6035         if(this.form.reader){
6036             var rs = this.form.reader.read(response);
6037             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
6038             return {
6039                 success : rs.success,
6040                 data : data
6041             };
6042         }
6043         return Roo.decode(response.responseText);
6044     }
6045 });
6046
6047 Roo.form.Action.ACTION_TYPES = {
6048     'load' : Roo.form.Action.Load,
6049     'submit' : Roo.form.Action.Submit
6050 };/*
6051  * - LGPL
6052  *
6053  * form
6054  * 
6055  */
6056
6057 /**
6058  * @class Roo.bootstrap.Form
6059  * @extends Roo.bootstrap.Component
6060  * Bootstrap Form class
6061  * @cfg {String} method  GET | POST (default POST)
6062  * @cfg {String} labelAlign top | left (default top)
6063   * @cfg {String} align left  | right - for navbars
6064
6065  * 
6066  * @constructor
6067  * Create a new Form
6068  * @param {Object} config The config object
6069  */
6070
6071
6072 Roo.bootstrap.Form = function(config){
6073     Roo.bootstrap.Form.superclass.constructor.call(this, config);
6074     this.addEvents({
6075         /**
6076          * @event clientvalidation
6077          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
6078          * @param {Form} this
6079          * @param {Boolean} valid true if the form has passed client-side validation
6080          */
6081         clientvalidation: true,
6082         /**
6083          * @event beforeaction
6084          * Fires before any action is performed. Return false to cancel the action.
6085          * @param {Form} this
6086          * @param {Action} action The action to be performed
6087          */
6088         beforeaction: true,
6089         /**
6090          * @event actionfailed
6091          * Fires when an action fails.
6092          * @param {Form} this
6093          * @param {Action} action The action that failed
6094          */
6095         actionfailed : true,
6096         /**
6097          * @event actioncomplete
6098          * Fires when an action is completed.
6099          * @param {Form} this
6100          * @param {Action} action The action that completed
6101          */
6102         actioncomplete : true
6103     });
6104     
6105 };
6106
6107 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
6108       
6109      /**
6110      * @cfg {String} method
6111      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
6112      */
6113     method : 'POST',
6114     /**
6115      * @cfg {String} url
6116      * The URL to use for form actions if one isn't supplied in the action options.
6117      */
6118     /**
6119      * @cfg {Boolean} fileUpload
6120      * Set to true if this form is a file upload.
6121      */
6122      
6123     /**
6124      * @cfg {Object} baseParams
6125      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
6126      */
6127       
6128     /**
6129      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
6130      */
6131     timeout: 30,
6132     /**
6133      * @cfg {Sting} align (left|right) for navbar forms
6134      */
6135     align : 'left',
6136
6137     // private
6138     activeAction : null,
6139  
6140     /**
6141      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6142      * element by passing it or its id or mask the form itself by passing in true.
6143      * @type Mixed
6144      */
6145     waitMsgTarget : false,
6146     
6147      
6148     
6149     /**
6150      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6151      * element by passing it or its id or mask the form itself by passing in true.
6152      * @type Mixed
6153      */
6154     
6155     getAutoCreate : function(){
6156         
6157         var cfg = {
6158             tag: 'form',
6159             method : this.method || 'POST',
6160             id : this.id || Roo.id(),
6161             cls : ''
6162         }
6163         if (this.parent().xtype.match(/^Nav/)) {
6164             cfg.cls = 'navbar-form navbar-' + this.align;
6165             
6166         }
6167         
6168         if (this.labelAlign == 'left' ) {
6169             cfg.cls += ' form-horizontal';
6170         }
6171         
6172         
6173         return cfg;
6174     },
6175     initEvents : function()
6176     {
6177         this.el.on('submit', this.onSubmit, this);
6178         // this was added as random key presses on the form where triggering form submit.
6179         this.el.on('keypress', function(e) {
6180             if (e.getCharCode() != 13) {
6181                 return true;
6182             }
6183             // we might need to allow it for textareas.. and some other items.
6184             // check e.getTarget().
6185             
6186             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
6187                 return true;
6188             }
6189         
6190             Roo.log("keypress blocked");
6191             
6192             e.preventDefault();
6193             return false;
6194         });
6195         
6196     },
6197     // private
6198     onSubmit : function(e){
6199         e.stopEvent();
6200     },
6201     
6202      /**
6203      * Returns true if client-side validation on the form is successful.
6204      * @return Boolean
6205      */
6206     isValid : function(){
6207         var items = this.getItems();
6208         var valid = true;
6209         items.each(function(f){
6210            if(!f.validate()){
6211                valid = false;
6212                
6213            }
6214         });
6215         return valid;
6216     },
6217     /**
6218      * Returns true if any fields in this form have changed since their original load.
6219      * @return Boolean
6220      */
6221     isDirty : function(){
6222         var dirty = false;
6223         var items = this.getItems();
6224         items.each(function(f){
6225            if(f.isDirty()){
6226                dirty = true;
6227                return false;
6228            }
6229            return true;
6230         });
6231         return dirty;
6232     },
6233      /**
6234      * Performs a predefined action (submit or load) or custom actions you define on this form.
6235      * @param {String} actionName The name of the action type
6236      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
6237      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
6238      * accept other config options):
6239      * <pre>
6240 Property          Type             Description
6241 ----------------  ---------------  ----------------------------------------------------------------------------------
6242 url               String           The url for the action (defaults to the form's url)
6243 method            String           The form method to use (defaults to the form's method, or POST if not defined)
6244 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
6245 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
6246                                    validate the form on the client (defaults to false)
6247      * </pre>
6248      * @return {BasicForm} this
6249      */
6250     doAction : function(action, options){
6251         if(typeof action == 'string'){
6252             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
6253         }
6254         if(this.fireEvent('beforeaction', this, action) !== false){
6255             this.beforeAction(action);
6256             action.run.defer(100, action);
6257         }
6258         return this;
6259     },
6260     
6261     // private
6262     beforeAction : function(action){
6263         var o = action.options;
6264         
6265         // not really supported yet.. ??
6266         
6267         //if(this.waitMsgTarget === true){
6268             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6269         //}else if(this.waitMsgTarget){
6270         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
6271         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
6272         //}else {
6273         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
6274        // }
6275          
6276     },
6277
6278     // private
6279     afterAction : function(action, success){
6280         this.activeAction = null;
6281         var o = action.options;
6282         
6283         //if(this.waitMsgTarget === true){
6284             this.el.unmask();
6285         //}else if(this.waitMsgTarget){
6286         //    this.waitMsgTarget.unmask();
6287         //}else{
6288         //    Roo.MessageBox.updateProgress(1);
6289         //    Roo.MessageBox.hide();
6290        // }
6291         // 
6292         if(success){
6293             if(o.reset){
6294                 this.reset();
6295             }
6296             Roo.callback(o.success, o.scope, [this, action]);
6297             this.fireEvent('actioncomplete', this, action);
6298             
6299         }else{
6300             
6301             // failure condition..
6302             // we have a scenario where updates need confirming.
6303             // eg. if a locking scenario exists..
6304             // we look for { errors : { needs_confirm : true }} in the response.
6305             if (
6306                 (typeof(action.result) != 'undefined')  &&
6307                 (typeof(action.result.errors) != 'undefined')  &&
6308                 (typeof(action.result.errors.needs_confirm) != 'undefined')
6309            ){
6310                 var _t = this;
6311                 Roo.log("not supported yet");
6312                  /*
6313                 
6314                 Roo.MessageBox.confirm(
6315                     "Change requires confirmation",
6316                     action.result.errorMsg,
6317                     function(r) {
6318                         if (r != 'yes') {
6319                             return;
6320                         }
6321                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
6322                     }
6323                     
6324                 );
6325                 */
6326                 
6327                 
6328                 return;
6329             }
6330             
6331             Roo.callback(o.failure, o.scope, [this, action]);
6332             // show an error message if no failed handler is set..
6333             if (!this.hasListener('actionfailed')) {
6334                 Roo.log("need to add dialog support");
6335                 /*
6336                 Roo.MessageBox.alert("Error",
6337                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
6338                         action.result.errorMsg :
6339                         "Saving Failed, please check your entries or try again"
6340                 );
6341                 */
6342             }
6343             
6344             this.fireEvent('actionfailed', this, action);
6345         }
6346         
6347     },
6348     /**
6349      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
6350      * @param {String} id The value to search for
6351      * @return Field
6352      */
6353     findField : function(id){
6354         var items = this.getItems();
6355         var field = items.get(id);
6356         if(!field){
6357              items.each(function(f){
6358                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
6359                     field = f;
6360                     return false;
6361                 }
6362                 return true;
6363             });
6364         }
6365         return field || null;
6366     },
6367      /**
6368      * Mark fields in this form invalid in bulk.
6369      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
6370      * @return {BasicForm} this
6371      */
6372     markInvalid : function(errors){
6373         if(errors instanceof Array){
6374             for(var i = 0, len = errors.length; i < len; i++){
6375                 var fieldError = errors[i];
6376                 var f = this.findField(fieldError.id);
6377                 if(f){
6378                     f.markInvalid(fieldError.msg);
6379                 }
6380             }
6381         }else{
6382             var field, id;
6383             for(id in errors){
6384                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
6385                     field.markInvalid(errors[id]);
6386                 }
6387             }
6388         }
6389         //Roo.each(this.childForms || [], function (f) {
6390         //    f.markInvalid(errors);
6391         //});
6392         
6393         return this;
6394     },
6395
6396     /**
6397      * Set values for fields in this form in bulk.
6398      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
6399      * @return {BasicForm} this
6400      */
6401     setValues : function(values){
6402         if(values instanceof Array){ // array of objects
6403             for(var i = 0, len = values.length; i < len; i++){
6404                 var v = values[i];
6405                 var f = this.findField(v.id);
6406                 if(f){
6407                     f.setValue(v.value);
6408                     if(this.trackResetOnLoad){
6409                         f.originalValue = f.getValue();
6410                     }
6411                 }
6412             }
6413         }else{ // object hash
6414             var field, id;
6415             for(id in values){
6416                 if(typeof values[id] != 'function' && (field = this.findField(id))){
6417                     
6418                     if (field.setFromData && 
6419                         field.valueField && 
6420                         field.displayField &&
6421                         // combos' with local stores can 
6422                         // be queried via setValue()
6423                         // to set their value..
6424                         (field.store && !field.store.isLocal)
6425                         ) {
6426                         // it's a combo
6427                         var sd = { };
6428                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
6429                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
6430                         field.setFromData(sd);
6431                         
6432                     } else {
6433                         field.setValue(values[id]);
6434                     }
6435                     
6436                     
6437                     if(this.trackResetOnLoad){
6438                         field.originalValue = field.getValue();
6439                     }
6440                 }
6441             }
6442         }
6443          
6444         //Roo.each(this.childForms || [], function (f) {
6445         //    f.setValues(values);
6446         //});
6447                 
6448         return this;
6449     },
6450
6451     /**
6452      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
6453      * they are returned as an array.
6454      * @param {Boolean} asString
6455      * @return {Object}
6456      */
6457     getValues : function(asString){
6458         //if (this.childForms) {
6459             // copy values from the child forms
6460         //    Roo.each(this.childForms, function (f) {
6461         //        this.setValues(f.getValues());
6462         //    }, this);
6463         //}
6464         
6465         
6466         
6467         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
6468         if(asString === true){
6469             return fs;
6470         }
6471         return Roo.urlDecode(fs);
6472     },
6473     
6474     /**
6475      * Returns the fields in this form as an object with key/value pairs. 
6476      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
6477      * @return {Object}
6478      */
6479     getFieldValues : function(with_hidden)
6480     {
6481         var items = this.getItems();
6482         var ret = {};
6483         items.each(function(f){
6484             if (!f.getName()) {
6485                 return;
6486             }
6487             var v = f.getValue();
6488             if (f.inputType =='radio') {
6489                 if (typeof(ret[f.getName()]) == 'undefined') {
6490                     ret[f.getName()] = ''; // empty..
6491                 }
6492                 
6493                 if (!f.el.dom.checked) {
6494                     return;
6495                     
6496                 }
6497                 v = f.el.dom.value;
6498                 
6499             }
6500             
6501             // not sure if this supported any more..
6502             if ((typeof(v) == 'object') && f.getRawValue) {
6503                 v = f.getRawValue() ; // dates..
6504             }
6505             // combo boxes where name != hiddenName...
6506             if (f.name != f.getName()) {
6507                 ret[f.name] = f.getRawValue();
6508             }
6509             ret[f.getName()] = v;
6510         });
6511         
6512         return ret;
6513     },
6514
6515     /**
6516      * Clears all invalid messages in this form.
6517      * @return {BasicForm} this
6518      */
6519     clearInvalid : function(){
6520         var items = this.getItems();
6521         
6522         items.each(function(f){
6523            f.clearInvalid();
6524         });
6525         
6526         
6527         
6528         return this;
6529     },
6530
6531     /**
6532      * Resets this form.
6533      * @return {BasicForm} this
6534      */
6535     reset : function(){
6536         var items = this.getItems();
6537         items.each(function(f){
6538             f.reset();
6539         });
6540         
6541         Roo.each(this.childForms || [], function (f) {
6542             f.reset();
6543         });
6544        
6545         
6546         return this;
6547     },
6548     getItems : function()
6549     {
6550         var r=new Roo.util.MixedCollection(false, function(o){
6551             return o.id || (o.id = Roo.id());
6552         });
6553         var iter = function(el) {
6554             if (el.inputEl) {
6555                 r.add(el);
6556             }
6557             if (!el.items) {
6558                 return;
6559             }
6560             Roo.each(el.items,function(e) {
6561                 iter(e);
6562             });
6563             
6564             
6565         };
6566         iter(this);
6567         return r;
6568         
6569         
6570         
6571         
6572     }
6573     
6574 });
6575
6576  
6577 /*
6578  * Based on:
6579  * Ext JS Library 1.1.1
6580  * Copyright(c) 2006-2007, Ext JS, LLC.
6581  *
6582  * Originally Released Under LGPL - original licence link has changed is not relivant.
6583  *
6584  * Fork - LGPL
6585  * <script type="text/javascript">
6586  */
6587 /**
6588  * @class Roo.form.VTypes
6589  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
6590  * @singleton
6591  */
6592 Roo.form.VTypes = function(){
6593     // closure these in so they are only created once.
6594     var alpha = /^[a-zA-Z_]+$/;
6595     var alphanum = /^[a-zA-Z0-9_]+$/;
6596     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
6597     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
6598
6599     // All these messages and functions are configurable
6600     return {
6601         /**
6602          * The function used to validate email addresses
6603          * @param {String} value The email address
6604          */
6605         'email' : function(v){
6606             return email.test(v);
6607         },
6608         /**
6609          * The error text to display when the email validation function returns false
6610          * @type String
6611          */
6612         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
6613         /**
6614          * The keystroke filter mask to be applied on email input
6615          * @type RegExp
6616          */
6617         'emailMask' : /[a-z0-9_\.\-@]/i,
6618
6619         /**
6620          * The function used to validate URLs
6621          * @param {String} value The URL
6622          */
6623         'url' : function(v){
6624             return url.test(v);
6625         },
6626         /**
6627          * The error text to display when the url validation function returns false
6628          * @type String
6629          */
6630         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
6631         
6632         /**
6633          * The function used to validate alpha values
6634          * @param {String} value The value
6635          */
6636         'alpha' : function(v){
6637             return alpha.test(v);
6638         },
6639         /**
6640          * The error text to display when the alpha validation function returns false
6641          * @type String
6642          */
6643         'alphaText' : 'This field should only contain letters and _',
6644         /**
6645          * The keystroke filter mask to be applied on alpha input
6646          * @type RegExp
6647          */
6648         'alphaMask' : /[a-z_]/i,
6649
6650         /**
6651          * The function used to validate alphanumeric values
6652          * @param {String} value The value
6653          */
6654         'alphanum' : function(v){
6655             return alphanum.test(v);
6656         },
6657         /**
6658          * The error text to display when the alphanumeric validation function returns false
6659          * @type String
6660          */
6661         'alphanumText' : 'This field should only contain letters, numbers and _',
6662         /**
6663          * The keystroke filter mask to be applied on alphanumeric input
6664          * @type RegExp
6665          */
6666         'alphanumMask' : /[a-z0-9_]/i
6667     };
6668 }();/*
6669  * - LGPL
6670  *
6671  * Input
6672  * 
6673  */
6674
6675 /**
6676  * @class Roo.bootstrap.Input
6677  * @extends Roo.bootstrap.Component
6678  * Bootstrap Input class
6679  * @cfg {Boolean} disabled is it disabled
6680  * @cfg {String} fieldLabel - the label associated
6681  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
6682  * @cfg {String} name name of the input
6683  * @cfg {string} fieldLabel - the label associated
6684  * @cfg {string}  inputType - input / file submit ...
6685  * @cfg {string} placeholder - placeholder to put in text.
6686  * @cfg {string}  before - input group add on before
6687  * @cfg {string} after - input group add on after
6688  * @cfg {string} size - (lg|sm) or leave empty..
6689  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
6690  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
6691  * @cfg {Number} md colspan out of 12 for computer-sized screens
6692  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
6693  * @cfg {string} value default value of the input
6694  * @cfg {Number} labelWidth set the width of label (0-12)
6695  * @cfg {String} labelAlign (top|left)
6696  * @cfg {Boolean} readOnly Specifies that the field should be read-only
6697  * @cfg {String} align (left|center|right) Default left
6698  * 
6699  * 
6700  * @constructor
6701  * Create a new Input
6702  * @param {Object} config The config object
6703  */
6704
6705 Roo.bootstrap.Input = function(config){
6706     Roo.bootstrap.Input.superclass.constructor.call(this, config);
6707    
6708         this.addEvents({
6709             /**
6710              * @event focus
6711              * Fires when this field receives input focus.
6712              * @param {Roo.form.Field} this
6713              */
6714             focus : true,
6715             /**
6716              * @event blur
6717              * Fires when this field loses input focus.
6718              * @param {Roo.form.Field} this
6719              */
6720             blur : true,
6721             /**
6722              * @event specialkey
6723              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
6724              * {@link Roo.EventObject#getKey} to determine which key was pressed.
6725              * @param {Roo.form.Field} this
6726              * @param {Roo.EventObject} e The event object
6727              */
6728             specialkey : true,
6729             /**
6730              * @event change
6731              * Fires just before the field blurs if the field value has changed.
6732              * @param {Roo.form.Field} this
6733              * @param {Mixed} newValue The new value
6734              * @param {Mixed} oldValue The original value
6735              */
6736             change : true,
6737             /**
6738              * @event invalid
6739              * Fires after the field has been marked as invalid.
6740              * @param {Roo.form.Field} this
6741              * @param {String} msg The validation message
6742              */
6743             invalid : true,
6744             /**
6745              * @event valid
6746              * Fires after the field has been validated with no errors.
6747              * @param {Roo.form.Field} this
6748              */
6749             valid : true,
6750              /**
6751              * @event keyup
6752              * Fires after the key up
6753              * @param {Roo.form.Field} this
6754              * @param {Roo.EventObject}  e The event Object
6755              */
6756             keyup : true
6757         });
6758 };
6759
6760 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
6761      /**
6762      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
6763       automatic validation (defaults to "keyup").
6764      */
6765     validationEvent : "keyup",
6766      /**
6767      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
6768      */
6769     validateOnBlur : true,
6770     /**
6771      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
6772      */
6773     validationDelay : 250,
6774      /**
6775      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
6776      */
6777     focusClass : "x-form-focus",  // not needed???
6778     
6779        
6780     /**
6781      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
6782      */
6783     invalidClass : "has-error",
6784     
6785     /**
6786      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
6787      */
6788     selectOnFocus : false,
6789     
6790      /**
6791      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
6792      */
6793     maskRe : null,
6794        /**
6795      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
6796      */
6797     vtype : null,
6798     
6799       /**
6800      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
6801      */
6802     disableKeyFilter : false,
6803     
6804        /**
6805      * @cfg {Boolean} disabled True to disable the field (defaults to false).
6806      */
6807     disabled : false,
6808      /**
6809      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
6810      */
6811     allowBlank : true,
6812     /**
6813      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
6814      */
6815     blankText : "This field is required",
6816     
6817      /**
6818      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
6819      */
6820     minLength : 0,
6821     /**
6822      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
6823      */
6824     maxLength : Number.MAX_VALUE,
6825     /**
6826      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
6827      */
6828     minLengthText : "The minimum length for this field is {0}",
6829     /**
6830      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
6831      */
6832     maxLengthText : "The maximum length for this field is {0}",
6833   
6834     
6835     /**
6836      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
6837      * If available, this function will be called only after the basic validators all return true, and will be passed the
6838      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
6839      */
6840     validator : null,
6841     /**
6842      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
6843      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
6844      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
6845      */
6846     regex : null,
6847     /**
6848      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
6849      */
6850     regexText : "",
6851     
6852     
6853     
6854     fieldLabel : '',
6855     inputType : 'text',
6856     
6857     name : false,
6858     placeholder: false,
6859     before : false,
6860     after : false,
6861     size : false,
6862     // private
6863     hasFocus : false,
6864     preventMark: false,
6865     isFormField : true,
6866     value : '',
6867     labelWidth : 2,
6868     labelAlign : false,
6869     readOnly : false,
6870     align : false,
6871     formatedValue : false,
6872     
6873     parentLabelAlign : function()
6874     {
6875         var parent = this;
6876         while (parent.parent()) {
6877             parent = parent.parent();
6878             if (typeof(parent.labelAlign) !='undefined') {
6879                 return parent.labelAlign;
6880             }
6881         }
6882         return 'left';
6883         
6884     },
6885     
6886     getAutoCreate : function(){
6887         
6888         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
6889         
6890         var id = Roo.id();
6891         
6892         var cfg = {};
6893         
6894         if(this.inputType != 'hidden'){
6895             cfg.cls = 'form-group' //input-group
6896         }
6897         
6898         var input =  {
6899             tag: 'input',
6900             id : id,
6901             type : this.inputType,
6902             value : this.value,
6903             cls : 'form-control',
6904             placeholder : this.placeholder || ''
6905             
6906         };
6907         
6908         if(this.align){
6909             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
6910         }
6911         
6912         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
6913             input.maxLength = this.maxLength;
6914         }
6915         
6916         if (this.disabled) {
6917             input.disabled=true;
6918         }
6919         
6920         if (this.readOnly) {
6921             input.readonly=true;
6922         }
6923         
6924         if (this.name) {
6925             input.name = this.name;
6926         }
6927         if (this.size) {
6928             input.cls += ' input-' + this.size;
6929         }
6930         var settings=this;
6931         ['xs','sm','md','lg'].map(function(size){
6932             if (settings[size]) {
6933                 cfg.cls += ' col-' + size + '-' + settings[size];
6934             }
6935         });
6936         
6937         var inputblock = input;
6938         
6939         if (this.before || this.after) {
6940             
6941             inputblock = {
6942                 cls : 'input-group',
6943                 cn :  [] 
6944             };
6945             if (this.before && typeof(this.before) == 'string') {
6946                 
6947                 inputblock.cn.push({
6948                     tag :'span',
6949                     cls : 'roo-input-before input-group-addon',
6950                     html : this.before
6951                 });
6952             }
6953             if (this.before && typeof(this.before) == 'object') {
6954                 this.before = Roo.factory(this.before);
6955                 Roo.log(this.before);
6956                 inputblock.cn.push({
6957                     tag :'span',
6958                     cls : 'roo-input-before input-group-' +
6959                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
6960                 });
6961             }
6962             
6963             inputblock.cn.push(input);
6964             
6965             if (this.after && typeof(this.after) == 'string') {
6966                 inputblock.cn.push({
6967                     tag :'span',
6968                     cls : 'roo-input-after input-group-addon',
6969                     html : this.after
6970                 });
6971             }
6972             if (this.after && typeof(this.after) == 'object') {
6973                 this.after = Roo.factory(this.after);
6974                 Roo.log(this.after);
6975                 inputblock.cn.push({
6976                     tag :'span',
6977                     cls : 'roo-input-after input-group-' +
6978                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
6979                 });
6980             }
6981         };
6982         
6983         if (align ==='left' && this.fieldLabel.length) {
6984                 Roo.log("left and has label");
6985                 cfg.cn = [
6986                     
6987                     {
6988                         tag: 'label',
6989                         'for' :  id,
6990                         cls : 'control-label col-sm-' + this.labelWidth,
6991                         html : this.fieldLabel
6992                         
6993                     },
6994                     {
6995                         cls : "col-sm-" + (12 - this.labelWidth), 
6996                         cn: [
6997                             inputblock
6998                         ]
6999                     }
7000                     
7001                 ];
7002         } else if ( this.fieldLabel.length) {
7003                 Roo.log(" label");
7004                  cfg.cn = [
7005                    
7006                     {
7007                         tag: 'label',
7008                         //cls : 'input-group-addon',
7009                         html : this.fieldLabel
7010                         
7011                     },
7012                     
7013                     inputblock
7014                     
7015                 ];
7016
7017         } else {
7018             
7019                 Roo.log(" no label && no align");
7020                 cfg.cn = [
7021                     
7022                         inputblock
7023                     
7024                 ];
7025                 
7026                 
7027         };
7028         Roo.log('input-parentType: ' + this.parentType);
7029         
7030         if (this.parentType === 'Navbar' &&  this.parent().bar) {
7031            cfg.cls += ' navbar-form';
7032            Roo.log(cfg);
7033         }
7034         
7035         return cfg;
7036         
7037     },
7038     /**
7039      * return the real input element.
7040      */
7041     inputEl: function ()
7042     {
7043         return this.el.select('input.form-control',true).first();
7044     },
7045     setDisabled : function(v)
7046     {
7047         var i  = this.inputEl().dom;
7048         if (!v) {
7049             i.removeAttribute('disabled');
7050             return;
7051             
7052         }
7053         i.setAttribute('disabled','true');
7054     },
7055     initEvents : function()
7056     {
7057         
7058         this.inputEl().on("keydown" , this.fireKey,  this);
7059         this.inputEl().on("focus", this.onFocus,  this);
7060         this.inputEl().on("blur", this.onBlur,  this);
7061         
7062         this.inputEl().relayEvent('keyup', this);
7063
7064         // reference to original value for reset
7065         this.originalValue = this.getValue();
7066         //Roo.form.TextField.superclass.initEvents.call(this);
7067         if(this.validationEvent == 'keyup'){
7068             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
7069             this.inputEl().on('keyup', this.filterValidation, this);
7070         }
7071         else if(this.validationEvent !== false){
7072             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
7073         }
7074         
7075         if(this.selectOnFocus){
7076             this.on("focus", this.preFocus, this);
7077             
7078         }
7079         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
7080             this.inputEl().on("keypress", this.filterKeys, this);
7081         }
7082        /* if(this.grow){
7083             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
7084             this.el.on("click", this.autoSize,  this);
7085         }
7086         */
7087         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
7088             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
7089         }
7090         
7091         if (typeof(this.before) == 'object') {
7092             this.before.render(this.el.select('.roo-input-before',true).first());
7093         }
7094         if (typeof(this.after) == 'object') {
7095             this.after.render(this.el.select('.roo-input-after',true).first());
7096         }
7097         
7098         
7099     },
7100     filterValidation : function(e){
7101         if(!e.isNavKeyPress()){
7102             this.validationTask.delay(this.validationDelay);
7103         }
7104     },
7105      /**
7106      * Validates the field value
7107      * @return {Boolean} True if the value is valid, else false
7108      */
7109     validate : function(){
7110         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
7111         if(this.disabled || this.validateValue(this.getRawValue())){
7112             this.clearInvalid();
7113             return true;
7114         }
7115         return false;
7116     },
7117     
7118     
7119     /**
7120      * Validates a value according to the field's validation rules and marks the field as invalid
7121      * if the validation fails
7122      * @param {Mixed} value The value to validate
7123      * @return {Boolean} True if the value is valid, else false
7124      */
7125     validateValue : function(value){
7126         if(value.length < 1)  { // if it's blank
7127              if(this.allowBlank){
7128                 this.clearInvalid();
7129                 return true;
7130              }else{
7131                 this.markInvalid(this.blankText);
7132                 return false;
7133              }
7134         }
7135         if(value.length < this.minLength){
7136             this.markInvalid(String.format(this.minLengthText, this.minLength));
7137             return false;
7138         }
7139         if(value.length > this.maxLength){
7140             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
7141             return false;
7142         }
7143         if(this.vtype){
7144             var vt = Roo.form.VTypes;
7145             if(!vt[this.vtype](value, this)){
7146                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
7147                 return false;
7148             }
7149         }
7150         if(typeof this.validator == "function"){
7151             var msg = this.validator(value);
7152             if(msg !== true){
7153                 this.markInvalid(msg);
7154                 return false;
7155             }
7156         }
7157         if(this.regex && !this.regex.test(value)){
7158             this.markInvalid(this.regexText);
7159             return false;
7160         }
7161         return true;
7162     },
7163
7164     
7165     
7166      // private
7167     fireKey : function(e){
7168         //Roo.log('field ' + e.getKey());
7169         if(e.isNavKeyPress()){
7170             this.fireEvent("specialkey", this, e);
7171         }
7172     },
7173     focus : function (selectText){
7174         if(this.rendered){
7175             this.inputEl().focus();
7176             if(selectText === true){
7177                 this.inputEl().dom.select();
7178             }
7179         }
7180         return this;
7181     } ,
7182     
7183     onFocus : function(){
7184         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7185            // this.el.addClass(this.focusClass);
7186         }
7187         if(!this.hasFocus){
7188             this.hasFocus = true;
7189             this.startValue = this.getValue();
7190             this.fireEvent("focus", this);
7191         }
7192     },
7193     
7194     beforeBlur : Roo.emptyFn,
7195
7196     
7197     // private
7198     onBlur : function(){
7199         this.beforeBlur();
7200         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7201             //this.el.removeClass(this.focusClass);
7202         }
7203         this.hasFocus = false;
7204         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
7205             this.validate();
7206         }
7207         var v = this.getValue();
7208         if(String(v) !== String(this.startValue)){
7209             this.fireEvent('change', this, v, this.startValue);
7210         }
7211         this.fireEvent("blur", this);
7212     },
7213     
7214     /**
7215      * Resets the current field value to the originally loaded value and clears any validation messages
7216      */
7217     reset : function(){
7218         this.setValue(this.originalValue);
7219         this.clearInvalid();
7220     },
7221      /**
7222      * Returns the name of the field
7223      * @return {Mixed} name The name field
7224      */
7225     getName: function(){
7226         return this.name;
7227     },
7228      /**
7229      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
7230      * @return {Mixed} value The field value
7231      */
7232     getValue : function(){
7233         
7234         var v = this.inputEl().getValue();
7235         
7236         return v;
7237     },
7238     /**
7239      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
7240      * @return {Mixed} value The field value
7241      */
7242     getRawValue : function(){
7243         var v = this.inputEl().getValue();
7244         
7245         return v;
7246     },
7247     
7248     /**
7249      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
7250      * @param {Mixed} value The value to set
7251      */
7252     setRawValue : function(v){
7253         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7254     },
7255     
7256     selectText : function(start, end){
7257         var v = this.getRawValue();
7258         if(v.length > 0){
7259             start = start === undefined ? 0 : start;
7260             end = end === undefined ? v.length : end;
7261             var d = this.inputEl().dom;
7262             if(d.setSelectionRange){
7263                 d.setSelectionRange(start, end);
7264             }else if(d.createTextRange){
7265                 var range = d.createTextRange();
7266                 range.moveStart("character", start);
7267                 range.moveEnd("character", v.length-end);
7268                 range.select();
7269             }
7270         }
7271     },
7272     
7273     /**
7274      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
7275      * @param {Mixed} value The value to set
7276      */
7277     setValue : function(v){
7278         this.value = v;
7279         if(this.rendered){
7280             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7281             this.validate();
7282         }
7283     },
7284     
7285     /*
7286     processValue : function(value){
7287         if(this.stripCharsRe){
7288             var newValue = value.replace(this.stripCharsRe, '');
7289             if(newValue !== value){
7290                 this.setRawValue(newValue);
7291                 return newValue;
7292             }
7293         }
7294         return value;
7295     },
7296   */
7297     preFocus : function(){
7298         
7299         if(this.selectOnFocus){
7300             this.inputEl().dom.select();
7301         }
7302     },
7303     filterKeys : function(e){
7304         var k = e.getKey();
7305         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
7306             return;
7307         }
7308         var c = e.getCharCode(), cc = String.fromCharCode(c);
7309         if(Roo.isIE && (e.isSpecialKey() || !cc)){
7310             return;
7311         }
7312         if(!this.maskRe.test(cc)){
7313             e.stopEvent();
7314         }
7315     },
7316      /**
7317      * Clear any invalid styles/messages for this field
7318      */
7319     clearInvalid : function(){
7320         
7321         if(!this.el || this.preventMark){ // not rendered
7322             return;
7323         }
7324         this.el.removeClass(this.invalidClass);
7325         /*
7326         switch(this.msgTarget){
7327             case 'qtip':
7328                 this.el.dom.qtip = '';
7329                 break;
7330             case 'title':
7331                 this.el.dom.title = '';
7332                 break;
7333             case 'under':
7334                 if(this.errorEl){
7335                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
7336                 }
7337                 break;
7338             case 'side':
7339                 if(this.errorIcon){
7340                     this.errorIcon.dom.qtip = '';
7341                     this.errorIcon.hide();
7342                     this.un('resize', this.alignErrorIcon, this);
7343                 }
7344                 break;
7345             default:
7346                 var t = Roo.getDom(this.msgTarget);
7347                 t.innerHTML = '';
7348                 t.style.display = 'none';
7349                 break;
7350         }
7351         */
7352         this.fireEvent('valid', this);
7353     },
7354      /**
7355      * Mark this field as invalid
7356      * @param {String} msg The validation message
7357      */
7358     markInvalid : function(msg){
7359         if(!this.el  || this.preventMark){ // not rendered
7360             return;
7361         }
7362         this.el.addClass(this.invalidClass);
7363         /*
7364         msg = msg || this.invalidText;
7365         switch(this.msgTarget){
7366             case 'qtip':
7367                 this.el.dom.qtip = msg;
7368                 this.el.dom.qclass = 'x-form-invalid-tip';
7369                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
7370                     Roo.QuickTips.enable();
7371                 }
7372                 break;
7373             case 'title':
7374                 this.el.dom.title = msg;
7375                 break;
7376             case 'under':
7377                 if(!this.errorEl){
7378                     var elp = this.el.findParent('.x-form-element', 5, true);
7379                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
7380                     this.errorEl.setWidth(elp.getWidth(true)-20);
7381                 }
7382                 this.errorEl.update(msg);
7383                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
7384                 break;
7385             case 'side':
7386                 if(!this.errorIcon){
7387                     var elp = this.el.findParent('.x-form-element', 5, true);
7388                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
7389                 }
7390                 this.alignErrorIcon();
7391                 this.errorIcon.dom.qtip = msg;
7392                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
7393                 this.errorIcon.show();
7394                 this.on('resize', this.alignErrorIcon, this);
7395                 break;
7396             default:
7397                 var t = Roo.getDom(this.msgTarget);
7398                 t.innerHTML = msg;
7399                 t.style.display = this.msgDisplay;
7400                 break;
7401         }
7402         */
7403         this.fireEvent('invalid', this, msg);
7404     },
7405     // private
7406     SafariOnKeyDown : function(event)
7407     {
7408         // this is a workaround for a password hang bug on chrome/ webkit.
7409         
7410         var isSelectAll = false;
7411         
7412         if(this.inputEl().dom.selectionEnd > 0){
7413             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
7414         }
7415         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
7416             event.preventDefault();
7417             this.setValue('');
7418             return;
7419         }
7420         
7421         if(isSelectAll){ // backspace and delete key
7422             
7423             event.preventDefault();
7424             // this is very hacky as keydown always get's upper case.
7425             //
7426             var cc = String.fromCharCode(event.getCharCode());
7427             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
7428             
7429         }
7430     },
7431     adjustWidth : function(tag, w){
7432         tag = tag.toLowerCase();
7433         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
7434             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
7435                 if(tag == 'input'){
7436                     return w + 2;
7437                 }
7438                 if(tag == 'textarea'){
7439                     return w-2;
7440                 }
7441             }else if(Roo.isOpera){
7442                 if(tag == 'input'){
7443                     return w + 2;
7444                 }
7445                 if(tag == 'textarea'){
7446                     return w-2;
7447                 }
7448             }
7449         }
7450         return w;
7451     }
7452     
7453 });
7454
7455  
7456 /*
7457  * - LGPL
7458  *
7459  * Input
7460  * 
7461  */
7462
7463 /**
7464  * @class Roo.bootstrap.TextArea
7465  * @extends Roo.bootstrap.Input
7466  * Bootstrap TextArea class
7467  * @cfg {Number} cols Specifies the visible width of a text area
7468  * @cfg {Number} rows Specifies the visible number of lines in a text area
7469  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
7470  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
7471  * @cfg {string} html text
7472  * 
7473  * @constructor
7474  * Create a new TextArea
7475  * @param {Object} config The config object
7476  */
7477
7478 Roo.bootstrap.TextArea = function(config){
7479     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
7480    
7481 };
7482
7483 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
7484      
7485     cols : false,
7486     rows : 5,
7487     readOnly : false,
7488     warp : 'soft',
7489     resize : false,
7490     value: false,
7491     html: false,
7492     
7493     getAutoCreate : function(){
7494         
7495         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7496         
7497         var id = Roo.id();
7498         
7499         var cfg = {};
7500         
7501         var input =  {
7502             tag: 'textarea',
7503             id : id,
7504             warp : this.warp,
7505             rows : this.rows,
7506             value : this.value || '',
7507             html: this.html || '',
7508             cls : 'form-control',
7509             placeholder : this.placeholder || '' 
7510             
7511         };
7512         
7513         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7514             input.maxLength = this.maxLength;
7515         }
7516         
7517         if(this.resize){
7518             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
7519         }
7520         
7521         if(this.cols){
7522             input.cols = this.cols;
7523         }
7524         
7525         if (this.readOnly) {
7526             input.readonly = true;
7527         }
7528         
7529         if (this.name) {
7530             input.name = this.name;
7531         }
7532         
7533         if (this.size) {
7534             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
7535         }
7536         
7537         var settings=this;
7538         ['xs','sm','md','lg'].map(function(size){
7539             if (settings[size]) {
7540                 cfg.cls += ' col-' + size + '-' + settings[size];
7541             }
7542         });
7543         
7544         var inputblock = input;
7545         
7546         if (this.before || this.after) {
7547             
7548             inputblock = {
7549                 cls : 'input-group',
7550                 cn :  [] 
7551             };
7552             if (this.before) {
7553                 inputblock.cn.push({
7554                     tag :'span',
7555                     cls : 'input-group-addon',
7556                     html : this.before
7557                 });
7558             }
7559             inputblock.cn.push(input);
7560             if (this.after) {
7561                 inputblock.cn.push({
7562                     tag :'span',
7563                     cls : 'input-group-addon',
7564                     html : this.after
7565                 });
7566             }
7567             
7568         }
7569         
7570         if (align ==='left' && this.fieldLabel.length) {
7571                 Roo.log("left and has label");
7572                 cfg.cn = [
7573                     
7574                     {
7575                         tag: 'label',
7576                         'for' :  id,
7577                         cls : 'control-label col-sm-' + this.labelWidth,
7578                         html : this.fieldLabel
7579                         
7580                     },
7581                     {
7582                         cls : "col-sm-" + (12 - this.labelWidth), 
7583                         cn: [
7584                             inputblock
7585                         ]
7586                     }
7587                     
7588                 ];
7589         } else if ( this.fieldLabel.length) {
7590                 Roo.log(" label");
7591                  cfg.cn = [
7592                    
7593                     {
7594                         tag: 'label',
7595                         //cls : 'input-group-addon',
7596                         html : this.fieldLabel
7597                         
7598                     },
7599                     
7600                     inputblock
7601                     
7602                 ];
7603
7604         } else {
7605             
7606                    Roo.log(" no label && no align");
7607                 cfg.cn = [
7608                     
7609                         inputblock
7610                     
7611                 ];
7612                 
7613                 
7614         }
7615         
7616         if (this.disabled) {
7617             input.disabled=true;
7618         }
7619         
7620         return cfg;
7621         
7622     },
7623     /**
7624      * return the real textarea element.
7625      */
7626     inputEl: function ()
7627     {
7628         return this.el.select('textarea.form-control',true).first();
7629     }
7630 });
7631
7632  
7633 /*
7634  * - LGPL
7635  *
7636  * trigger field - base class for combo..
7637  * 
7638  */
7639  
7640 /**
7641  * @class Roo.bootstrap.TriggerField
7642  * @extends Roo.bootstrap.Input
7643  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
7644  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
7645  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
7646  * for which you can provide a custom implementation.  For example:
7647  * <pre><code>
7648 var trigger = new Roo.bootstrap.TriggerField();
7649 trigger.onTriggerClick = myTriggerFn;
7650 trigger.applyTo('my-field');
7651 </code></pre>
7652  *
7653  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
7654  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
7655  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
7656  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
7657  * @constructor
7658  * Create a new TriggerField.
7659  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
7660  * to the base TextField)
7661  */
7662 Roo.bootstrap.TriggerField = function(config){
7663     this.mimicing = false;
7664     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
7665 };
7666
7667 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
7668     /**
7669      * @cfg {String} triggerClass A CSS class to apply to the trigger
7670      */
7671      /**
7672      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
7673      */
7674     hideTrigger:false,
7675
7676     /** @cfg {Boolean} grow @hide */
7677     /** @cfg {Number} growMin @hide */
7678     /** @cfg {Number} growMax @hide */
7679
7680     /**
7681      * @hide 
7682      * @method
7683      */
7684     autoSize: Roo.emptyFn,
7685     // private
7686     monitorTab : true,
7687     // private
7688     deferHeight : true,
7689
7690     
7691     actionMode : 'wrap',
7692     
7693     
7694     
7695     getAutoCreate : function(){
7696        
7697         var align = this.labelAlign || this.parentLabelAlign();
7698         
7699         var id = Roo.id();
7700         
7701         var cfg = {
7702             cls: 'form-group' //input-group
7703         };
7704         
7705         
7706         var input =  {
7707             tag: 'input',
7708             id : id,
7709             type : this.inputType,
7710             cls : 'form-control',
7711             autocomplete: 'off',
7712             placeholder : this.placeholder || '' 
7713             
7714         };
7715         if (this.name) {
7716             input.name = this.name;
7717         }
7718         if (this.size) {
7719             input.cls += ' input-' + this.size;
7720         }
7721         
7722         if (this.disabled) {
7723             input.disabled=true;
7724         }
7725         
7726         var inputblock = input;
7727         
7728         if (this.before || this.after) {
7729             
7730             inputblock = {
7731                 cls : 'input-group',
7732                 cn :  [] 
7733             };
7734             if (this.before) {
7735                 inputblock.cn.push({
7736                     tag :'span',
7737                     cls : 'input-group-addon',
7738                     html : this.before
7739                 });
7740             }
7741             inputblock.cn.push(input);
7742             if (this.after) {
7743                 inputblock.cn.push({
7744                     tag :'span',
7745                     cls : 'input-group-addon',
7746                     html : this.after
7747                 });
7748             }
7749             
7750         };
7751         
7752         var box = {
7753             tag: 'div',
7754             cn: [
7755                 {
7756                     tag: 'input',
7757                     type : 'hidden',
7758                     cls: 'form-hidden-field'
7759                 },
7760                 inputblock
7761             ]
7762             
7763         };
7764         
7765         if(this.multiple){
7766             Roo.log('multiple');
7767             
7768             box = {
7769                 tag: 'div',
7770                 cn: [
7771                     {
7772                         tag: 'input',
7773                         type : 'hidden',
7774                         cls: 'form-hidden-field'
7775                     },
7776                     {
7777                         tag: 'ul',
7778                         cls: 'select2-choices',
7779                         cn:[
7780                             {
7781                                 tag: 'li',
7782                                 cls: 'select2-search-field',
7783                                 cn: [
7784
7785                                     inputblock
7786                                 ]
7787                             }
7788                         ]
7789                     }
7790                 ]
7791             }
7792         };
7793         
7794         var combobox = {
7795             cls: 'select2-container input-group',
7796             cn: [
7797                 box
7798 //                {
7799 //                    tag: 'ul',
7800 //                    cls: 'typeahead typeahead-long dropdown-menu',
7801 //                    style: 'display:none'
7802 //                }
7803             ]
7804         };
7805         
7806         if(!this.multiple && this.showToggleBtn){
7807             combobox.cn.push({
7808                 tag :'span',
7809                 cls : 'input-group-addon btn dropdown-toggle',
7810                 cn : [
7811                     {
7812                         tag: 'span',
7813                         cls: 'caret'
7814                     },
7815                     {
7816                         tag: 'span',
7817                         cls: 'combobox-clear',
7818                         cn  : [
7819                             {
7820                                 tag : 'i',
7821                                 cls: 'icon-remove'
7822                             }
7823                         ]
7824                     }
7825                 ]
7826
7827             })
7828         }
7829         
7830         if(this.multiple){
7831             combobox.cls += ' select2-container-multi';
7832         }
7833         
7834         if (align ==='left' && this.fieldLabel.length) {
7835             
7836                 Roo.log("left and has label");
7837                 cfg.cn = [
7838                     
7839                     {
7840                         tag: 'label',
7841                         'for' :  id,
7842                         cls : 'control-label col-sm-' + this.labelWidth,
7843                         html : this.fieldLabel
7844                         
7845                     },
7846                     {
7847                         cls : "col-sm-" + (12 - this.labelWidth), 
7848                         cn: [
7849                             combobox
7850                         ]
7851                     }
7852                     
7853                 ];
7854         } else if ( this.fieldLabel.length) {
7855                 Roo.log(" label");
7856                  cfg.cn = [
7857                    
7858                     {
7859                         tag: 'label',
7860                         //cls : 'input-group-addon',
7861                         html : this.fieldLabel
7862                         
7863                     },
7864                     
7865                     combobox
7866                     
7867                 ];
7868
7869         } else {
7870             
7871                 Roo.log(" no label && no align");
7872                 cfg = combobox
7873                      
7874                 
7875         }
7876          
7877         var settings=this;
7878         ['xs','sm','md','lg'].map(function(size){
7879             if (settings[size]) {
7880                 cfg.cls += ' col-' + size + '-' + settings[size];
7881             }
7882         });
7883         
7884         return cfg;
7885         
7886     },
7887     
7888     
7889     
7890     // private
7891     onResize : function(w, h){
7892 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
7893 //        if(typeof w == 'number'){
7894 //            var x = w - this.trigger.getWidth();
7895 //            this.inputEl().setWidth(this.adjustWidth('input', x));
7896 //            this.trigger.setStyle('left', x+'px');
7897 //        }
7898     },
7899
7900     // private
7901     adjustSize : Roo.BoxComponent.prototype.adjustSize,
7902
7903     // private
7904     getResizeEl : function(){
7905         return this.inputEl();
7906     },
7907
7908     // private
7909     getPositionEl : function(){
7910         return this.inputEl();
7911     },
7912
7913     // private
7914     alignErrorIcon : function(){
7915         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
7916     },
7917
7918     // private
7919     initEvents : function(){
7920         
7921         this.createList();
7922         
7923         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
7924         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
7925         if(!this.multiple && this.showToggleBtn){
7926             this.trigger = this.el.select('span.dropdown-toggle',true).first();
7927             if(this.hideTrigger){
7928                 this.trigger.setDisplayed(false);
7929             }
7930             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
7931         }
7932         
7933         if(this.multiple){
7934             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
7935         }
7936         
7937         //this.trigger.addClassOnOver('x-form-trigger-over');
7938         //this.trigger.addClassOnClick('x-form-trigger-click');
7939         
7940         //if(!this.width){
7941         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
7942         //}
7943     },
7944     
7945     createList : function()
7946     {
7947         this.list = Roo.get(document.body).createChild({
7948             tag: 'ul',
7949             cls: 'typeahead typeahead-long dropdown-menu',
7950             style: 'display:none'
7951         });
7952         
7953         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
7954         
7955     },
7956
7957     // private
7958     initTrigger : function(){
7959        
7960     },
7961
7962     // private
7963     onDestroy : function(){
7964         if(this.trigger){
7965             this.trigger.removeAllListeners();
7966           //  this.trigger.remove();
7967         }
7968         //if(this.wrap){
7969         //    this.wrap.remove();
7970         //}
7971         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
7972     },
7973
7974     // private
7975     onFocus : function(){
7976         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
7977         /*
7978         if(!this.mimicing){
7979             this.wrap.addClass('x-trigger-wrap-focus');
7980             this.mimicing = true;
7981             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
7982             if(this.monitorTab){
7983                 this.el.on("keydown", this.checkTab, this);
7984             }
7985         }
7986         */
7987     },
7988
7989     // private
7990     checkTab : function(e){
7991         if(e.getKey() == e.TAB){
7992             this.triggerBlur();
7993         }
7994     },
7995
7996     // private
7997     onBlur : function(){
7998         // do nothing
7999     },
8000
8001     // private
8002     mimicBlur : function(e, t){
8003         /*
8004         if(!this.wrap.contains(t) && this.validateBlur()){
8005             this.triggerBlur();
8006         }
8007         */
8008     },
8009
8010     // private
8011     triggerBlur : function(){
8012         this.mimicing = false;
8013         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
8014         if(this.monitorTab){
8015             this.el.un("keydown", this.checkTab, this);
8016         }
8017         //this.wrap.removeClass('x-trigger-wrap-focus');
8018         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
8019     },
8020
8021     // private
8022     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
8023     validateBlur : function(e, t){
8024         return true;
8025     },
8026
8027     // private
8028     onDisable : function(){
8029         this.inputEl().dom.disabled = true;
8030         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
8031         //if(this.wrap){
8032         //    this.wrap.addClass('x-item-disabled');
8033         //}
8034     },
8035
8036     // private
8037     onEnable : function(){
8038         this.inputEl().dom.disabled = false;
8039         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
8040         //if(this.wrap){
8041         //    this.el.removeClass('x-item-disabled');
8042         //}
8043     },
8044
8045     // private
8046     onShow : function(){
8047         var ae = this.getActionEl();
8048         
8049         if(ae){
8050             ae.dom.style.display = '';
8051             ae.dom.style.visibility = 'visible';
8052         }
8053     },
8054
8055     // private
8056     
8057     onHide : function(){
8058         var ae = this.getActionEl();
8059         ae.dom.style.display = 'none';
8060     },
8061
8062     /**
8063      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
8064      * by an implementing function.
8065      * @method
8066      * @param {EventObject} e
8067      */
8068     onTriggerClick : Roo.emptyFn
8069 });
8070  /*
8071  * Based on:
8072  * Ext JS Library 1.1.1
8073  * Copyright(c) 2006-2007, Ext JS, LLC.
8074  *
8075  * Originally Released Under LGPL - original licence link has changed is not relivant.
8076  *
8077  * Fork - LGPL
8078  * <script type="text/javascript">
8079  */
8080
8081
8082 /**
8083  * @class Roo.data.SortTypes
8084  * @singleton
8085  * Defines the default sorting (casting?) comparison functions used when sorting data.
8086  */
8087 Roo.data.SortTypes = {
8088     /**
8089      * Default sort that does nothing
8090      * @param {Mixed} s The value being converted
8091      * @return {Mixed} The comparison value
8092      */
8093     none : function(s){
8094         return s;
8095     },
8096     
8097     /**
8098      * The regular expression used to strip tags
8099      * @type {RegExp}
8100      * @property
8101      */
8102     stripTagsRE : /<\/?[^>]+>/gi,
8103     
8104     /**
8105      * Strips all HTML tags to sort on text only
8106      * @param {Mixed} s The value being converted
8107      * @return {String} The comparison value
8108      */
8109     asText : function(s){
8110         return String(s).replace(this.stripTagsRE, "");
8111     },
8112     
8113     /**
8114      * Strips all HTML tags to sort on text only - Case insensitive
8115      * @param {Mixed} s The value being converted
8116      * @return {String} The comparison value
8117      */
8118     asUCText : function(s){
8119         return String(s).toUpperCase().replace(this.stripTagsRE, "");
8120     },
8121     
8122     /**
8123      * Case insensitive string
8124      * @param {Mixed} s The value being converted
8125      * @return {String} The comparison value
8126      */
8127     asUCString : function(s) {
8128         return String(s).toUpperCase();
8129     },
8130     
8131     /**
8132      * Date sorting
8133      * @param {Mixed} s The value being converted
8134      * @return {Number} The comparison value
8135      */
8136     asDate : function(s) {
8137         if(!s){
8138             return 0;
8139         }
8140         if(s instanceof Date){
8141             return s.getTime();
8142         }
8143         return Date.parse(String(s));
8144     },
8145     
8146     /**
8147      * Float sorting
8148      * @param {Mixed} s The value being converted
8149      * @return {Float} The comparison value
8150      */
8151     asFloat : function(s) {
8152         var val = parseFloat(String(s).replace(/,/g, ""));
8153         if(isNaN(val)) val = 0;
8154         return val;
8155     },
8156     
8157     /**
8158      * Integer sorting
8159      * @param {Mixed} s The value being converted
8160      * @return {Number} The comparison value
8161      */
8162     asInt : function(s) {
8163         var val = parseInt(String(s).replace(/,/g, ""));
8164         if(isNaN(val)) val = 0;
8165         return val;
8166     }
8167 };/*
8168  * Based on:
8169  * Ext JS Library 1.1.1
8170  * Copyright(c) 2006-2007, Ext JS, LLC.
8171  *
8172  * Originally Released Under LGPL - original licence link has changed is not relivant.
8173  *
8174  * Fork - LGPL
8175  * <script type="text/javascript">
8176  */
8177
8178 /**
8179 * @class Roo.data.Record
8180  * Instances of this class encapsulate both record <em>definition</em> information, and record
8181  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
8182  * to access Records cached in an {@link Roo.data.Store} object.<br>
8183  * <p>
8184  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
8185  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
8186  * objects.<br>
8187  * <p>
8188  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
8189  * @constructor
8190  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
8191  * {@link #create}. The parameters are the same.
8192  * @param {Array} data An associative Array of data values keyed by the field name.
8193  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
8194  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
8195  * not specified an integer id is generated.
8196  */
8197 Roo.data.Record = function(data, id){
8198     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
8199     this.data = data;
8200 };
8201
8202 /**
8203  * Generate a constructor for a specific record layout.
8204  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
8205  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
8206  * Each field definition object may contain the following properties: <ul>
8207  * <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,
8208  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
8209  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
8210  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
8211  * is being used, then this is a string containing the javascript expression to reference the data relative to 
8212  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
8213  * to the data item relative to the record element. If the mapping expression is the same as the field name,
8214  * this may be omitted.</p></li>
8215  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
8216  * <ul><li>auto (Default, implies no conversion)</li>
8217  * <li>string</li>
8218  * <li>int</li>
8219  * <li>float</li>
8220  * <li>boolean</li>
8221  * <li>date</li></ul></p></li>
8222  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
8223  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
8224  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
8225  * by the Reader into an object that will be stored in the Record. It is passed the
8226  * following parameters:<ul>
8227  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
8228  * </ul></p></li>
8229  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
8230  * </ul>
8231  * <br>usage:<br><pre><code>
8232 var TopicRecord = Roo.data.Record.create(
8233     {name: 'title', mapping: 'topic_title'},
8234     {name: 'author', mapping: 'username'},
8235     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
8236     {name: 'lastPost', mapping: 'post_time', type: 'date'},
8237     {name: 'lastPoster', mapping: 'user2'},
8238     {name: 'excerpt', mapping: 'post_text'}
8239 );
8240
8241 var myNewRecord = new TopicRecord({
8242     title: 'Do my job please',
8243     author: 'noobie',
8244     totalPosts: 1,
8245     lastPost: new Date(),
8246     lastPoster: 'Animal',
8247     excerpt: 'No way dude!'
8248 });
8249 myStore.add(myNewRecord);
8250 </code></pre>
8251  * @method create
8252  * @static
8253  */
8254 Roo.data.Record.create = function(o){
8255     var f = function(){
8256         f.superclass.constructor.apply(this, arguments);
8257     };
8258     Roo.extend(f, Roo.data.Record);
8259     var p = f.prototype;
8260     p.fields = new Roo.util.MixedCollection(false, function(field){
8261         return field.name;
8262     });
8263     for(var i = 0, len = o.length; i < len; i++){
8264         p.fields.add(new Roo.data.Field(o[i]));
8265     }
8266     f.getField = function(name){
8267         return p.fields.get(name);  
8268     };
8269     return f;
8270 };
8271
8272 Roo.data.Record.AUTO_ID = 1000;
8273 Roo.data.Record.EDIT = 'edit';
8274 Roo.data.Record.REJECT = 'reject';
8275 Roo.data.Record.COMMIT = 'commit';
8276
8277 Roo.data.Record.prototype = {
8278     /**
8279      * Readonly flag - true if this record has been modified.
8280      * @type Boolean
8281      */
8282     dirty : false,
8283     editing : false,
8284     error: null,
8285     modified: null,
8286
8287     // private
8288     join : function(store){
8289         this.store = store;
8290     },
8291
8292     /**
8293      * Set the named field to the specified value.
8294      * @param {String} name The name of the field to set.
8295      * @param {Object} value The value to set the field to.
8296      */
8297     set : function(name, value){
8298         if(this.data[name] == value){
8299             return;
8300         }
8301         this.dirty = true;
8302         if(!this.modified){
8303             this.modified = {};
8304         }
8305         if(typeof this.modified[name] == 'undefined'){
8306             this.modified[name] = this.data[name];
8307         }
8308         this.data[name] = value;
8309         if(!this.editing && this.store){
8310             this.store.afterEdit(this);
8311         }       
8312     },
8313
8314     /**
8315      * Get the value of the named field.
8316      * @param {String} name The name of the field to get the value of.
8317      * @return {Object} The value of the field.
8318      */
8319     get : function(name){
8320         return this.data[name]; 
8321     },
8322
8323     // private
8324     beginEdit : function(){
8325         this.editing = true;
8326         this.modified = {}; 
8327     },
8328
8329     // private
8330     cancelEdit : function(){
8331         this.editing = false;
8332         delete this.modified;
8333     },
8334
8335     // private
8336     endEdit : function(){
8337         this.editing = false;
8338         if(this.dirty && this.store){
8339             this.store.afterEdit(this);
8340         }
8341     },
8342
8343     /**
8344      * Usually called by the {@link Roo.data.Store} which owns the Record.
8345      * Rejects all changes made to the Record since either creation, or the last commit operation.
8346      * Modified fields are reverted to their original values.
8347      * <p>
8348      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8349      * of reject operations.
8350      */
8351     reject : function(){
8352         var m = this.modified;
8353         for(var n in m){
8354             if(typeof m[n] != "function"){
8355                 this.data[n] = m[n];
8356             }
8357         }
8358         this.dirty = false;
8359         delete this.modified;
8360         this.editing = false;
8361         if(this.store){
8362             this.store.afterReject(this);
8363         }
8364     },
8365
8366     /**
8367      * Usually called by the {@link Roo.data.Store} which owns the Record.
8368      * Commits all changes made to the Record since either creation, or the last commit operation.
8369      * <p>
8370      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8371      * of commit operations.
8372      */
8373     commit : function(){
8374         this.dirty = false;
8375         delete this.modified;
8376         this.editing = false;
8377         if(this.store){
8378             this.store.afterCommit(this);
8379         }
8380     },
8381
8382     // private
8383     hasError : function(){
8384         return this.error != null;
8385     },
8386
8387     // private
8388     clearError : function(){
8389         this.error = null;
8390     },
8391
8392     /**
8393      * Creates a copy of this record.
8394      * @param {String} id (optional) A new record id if you don't want to use this record's id
8395      * @return {Record}
8396      */
8397     copy : function(newId) {
8398         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
8399     }
8400 };/*
8401  * Based on:
8402  * Ext JS Library 1.1.1
8403  * Copyright(c) 2006-2007, Ext JS, LLC.
8404  *
8405  * Originally Released Under LGPL - original licence link has changed is not relivant.
8406  *
8407  * Fork - LGPL
8408  * <script type="text/javascript">
8409  */
8410
8411
8412
8413 /**
8414  * @class Roo.data.Store
8415  * @extends Roo.util.Observable
8416  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
8417  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
8418  * <p>
8419  * 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
8420  * has no knowledge of the format of the data returned by the Proxy.<br>
8421  * <p>
8422  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
8423  * instances from the data object. These records are cached and made available through accessor functions.
8424  * @constructor
8425  * Creates a new Store.
8426  * @param {Object} config A config object containing the objects needed for the Store to access data,
8427  * and read the data into Records.
8428  */
8429 Roo.data.Store = function(config){
8430     this.data = new Roo.util.MixedCollection(false);
8431     this.data.getKey = function(o){
8432         return o.id;
8433     };
8434     this.baseParams = {};
8435     // private
8436     this.paramNames = {
8437         "start" : "start",
8438         "limit" : "limit",
8439         "sort" : "sort",
8440         "dir" : "dir",
8441         "multisort" : "_multisort"
8442     };
8443
8444     if(config && config.data){
8445         this.inlineData = config.data;
8446         delete config.data;
8447     }
8448
8449     Roo.apply(this, config);
8450     
8451     if(this.reader){ // reader passed
8452         this.reader = Roo.factory(this.reader, Roo.data);
8453         this.reader.xmodule = this.xmodule || false;
8454         if(!this.recordType){
8455             this.recordType = this.reader.recordType;
8456         }
8457         if(this.reader.onMetaChange){
8458             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
8459         }
8460     }
8461
8462     if(this.recordType){
8463         this.fields = this.recordType.prototype.fields;
8464     }
8465     this.modified = [];
8466
8467     this.addEvents({
8468         /**
8469          * @event datachanged
8470          * Fires when the data cache has changed, and a widget which is using this Store
8471          * as a Record cache should refresh its view.
8472          * @param {Store} this
8473          */
8474         datachanged : true,
8475         /**
8476          * @event metachange
8477          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
8478          * @param {Store} this
8479          * @param {Object} meta The JSON metadata
8480          */
8481         metachange : true,
8482         /**
8483          * @event add
8484          * Fires when Records have been added to the Store
8485          * @param {Store} this
8486          * @param {Roo.data.Record[]} records The array of Records added
8487          * @param {Number} index The index at which the record(s) were added
8488          */
8489         add : true,
8490         /**
8491          * @event remove
8492          * Fires when a Record has been removed from the Store
8493          * @param {Store} this
8494          * @param {Roo.data.Record} record The Record that was removed
8495          * @param {Number} index The index at which the record was removed
8496          */
8497         remove : true,
8498         /**
8499          * @event update
8500          * Fires when a Record has been updated
8501          * @param {Store} this
8502          * @param {Roo.data.Record} record The Record that was updated
8503          * @param {String} operation The update operation being performed.  Value may be one of:
8504          * <pre><code>
8505  Roo.data.Record.EDIT
8506  Roo.data.Record.REJECT
8507  Roo.data.Record.COMMIT
8508          * </code></pre>
8509          */
8510         update : true,
8511         /**
8512          * @event clear
8513          * Fires when the data cache has been cleared.
8514          * @param {Store} this
8515          */
8516         clear : true,
8517         /**
8518          * @event beforeload
8519          * Fires before a request is made for a new data object.  If the beforeload handler returns false
8520          * the load action will be canceled.
8521          * @param {Store} this
8522          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8523          */
8524         beforeload : true,
8525         /**
8526          * @event beforeloadadd
8527          * Fires after a new set of Records has been loaded.
8528          * @param {Store} this
8529          * @param {Roo.data.Record[]} records The Records that were loaded
8530          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8531          */
8532         beforeloadadd : true,
8533         /**
8534          * @event load
8535          * Fires after a new set of Records has been loaded, before they are added to the store.
8536          * @param {Store} this
8537          * @param {Roo.data.Record[]} records The Records that were loaded
8538          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8539          * @params {Object} return from reader
8540          */
8541         load : true,
8542         /**
8543          * @event loadexception
8544          * Fires if an exception occurs in the Proxy during loading.
8545          * Called with the signature of the Proxy's "loadexception" event.
8546          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
8547          * 
8548          * @param {Proxy} 
8549          * @param {Object} return from JsonData.reader() - success, totalRecords, records
8550          * @param {Object} load options 
8551          * @param {Object} jsonData from your request (normally this contains the Exception)
8552          */
8553         loadexception : true
8554     });
8555     
8556     if(this.proxy){
8557         this.proxy = Roo.factory(this.proxy, Roo.data);
8558         this.proxy.xmodule = this.xmodule || false;
8559         this.relayEvents(this.proxy,  ["loadexception"]);
8560     }
8561     this.sortToggle = {};
8562     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
8563
8564     Roo.data.Store.superclass.constructor.call(this);
8565
8566     if(this.inlineData){
8567         this.loadData(this.inlineData);
8568         delete this.inlineData;
8569     }
8570 };
8571
8572 Roo.extend(Roo.data.Store, Roo.util.Observable, {
8573      /**
8574     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
8575     * without a remote query - used by combo/forms at present.
8576     */
8577     
8578     /**
8579     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
8580     */
8581     /**
8582     * @cfg {Array} data Inline data to be loaded when the store is initialized.
8583     */
8584     /**
8585     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
8586     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
8587     */
8588     /**
8589     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
8590     * on any HTTP request
8591     */
8592     /**
8593     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
8594     */
8595     /**
8596     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
8597     */
8598     multiSort: false,
8599     /**
8600     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
8601     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
8602     */
8603     remoteSort : false,
8604
8605     /**
8606     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
8607      * loaded or when a record is removed. (defaults to false).
8608     */
8609     pruneModifiedRecords : false,
8610
8611     // private
8612     lastOptions : null,
8613
8614     /**
8615      * Add Records to the Store and fires the add event.
8616      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8617      */
8618     add : function(records){
8619         records = [].concat(records);
8620         for(var i = 0, len = records.length; i < len; i++){
8621             records[i].join(this);
8622         }
8623         var index = this.data.length;
8624         this.data.addAll(records);
8625         this.fireEvent("add", this, records, index);
8626     },
8627
8628     /**
8629      * Remove a Record from the Store and fires the remove event.
8630      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
8631      */
8632     remove : function(record){
8633         var index = this.data.indexOf(record);
8634         this.data.removeAt(index);
8635         if(this.pruneModifiedRecords){
8636             this.modified.remove(record);
8637         }
8638         this.fireEvent("remove", this, record, index);
8639     },
8640
8641     /**
8642      * Remove all Records from the Store and fires the clear event.
8643      */
8644     removeAll : function(){
8645         this.data.clear();
8646         if(this.pruneModifiedRecords){
8647             this.modified = [];
8648         }
8649         this.fireEvent("clear", this);
8650     },
8651
8652     /**
8653      * Inserts Records to the Store at the given index and fires the add event.
8654      * @param {Number} index The start index at which to insert the passed Records.
8655      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8656      */
8657     insert : function(index, records){
8658         records = [].concat(records);
8659         for(var i = 0, len = records.length; i < len; i++){
8660             this.data.insert(index, records[i]);
8661             records[i].join(this);
8662         }
8663         this.fireEvent("add", this, records, index);
8664     },
8665
8666     /**
8667      * Get the index within the cache of the passed Record.
8668      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
8669      * @return {Number} The index of the passed Record. Returns -1 if not found.
8670      */
8671     indexOf : function(record){
8672         return this.data.indexOf(record);
8673     },
8674
8675     /**
8676      * Get the index within the cache of the Record with the passed id.
8677      * @param {String} id The id of the Record to find.
8678      * @return {Number} The index of the Record. Returns -1 if not found.
8679      */
8680     indexOfId : function(id){
8681         return this.data.indexOfKey(id);
8682     },
8683
8684     /**
8685      * Get the Record with the specified id.
8686      * @param {String} id The id of the Record to find.
8687      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
8688      */
8689     getById : function(id){
8690         return this.data.key(id);
8691     },
8692
8693     /**
8694      * Get the Record at the specified index.
8695      * @param {Number} index The index of the Record to find.
8696      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
8697      */
8698     getAt : function(index){
8699         return this.data.itemAt(index);
8700     },
8701
8702     /**
8703      * Returns a range of Records between specified indices.
8704      * @param {Number} startIndex (optional) The starting index (defaults to 0)
8705      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
8706      * @return {Roo.data.Record[]} An array of Records
8707      */
8708     getRange : function(start, end){
8709         return this.data.getRange(start, end);
8710     },
8711
8712     // private
8713     storeOptions : function(o){
8714         o = Roo.apply({}, o);
8715         delete o.callback;
8716         delete o.scope;
8717         this.lastOptions = o;
8718     },
8719
8720     /**
8721      * Loads the Record cache from the configured Proxy using the configured Reader.
8722      * <p>
8723      * If using remote paging, then the first load call must specify the <em>start</em>
8724      * and <em>limit</em> properties in the options.params property to establish the initial
8725      * position within the dataset, and the number of Records to cache on each read from the Proxy.
8726      * <p>
8727      * <strong>It is important to note that for remote data sources, loading is asynchronous,
8728      * and this call will return before the new data has been loaded. Perform any post-processing
8729      * in a callback function, or in a "load" event handler.</strong>
8730      * <p>
8731      * @param {Object} options An object containing properties which control loading options:<ul>
8732      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
8733      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
8734      * passed the following arguments:<ul>
8735      * <li>r : Roo.data.Record[]</li>
8736      * <li>options: Options object from the load call</li>
8737      * <li>success: Boolean success indicator</li></ul></li>
8738      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
8739      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
8740      * </ul>
8741      */
8742     load : function(options){
8743         options = options || {};
8744         if(this.fireEvent("beforeload", this, options) !== false){
8745             this.storeOptions(options);
8746             var p = Roo.apply(options.params || {}, this.baseParams);
8747             // if meta was not loaded from remote source.. try requesting it.
8748             if (!this.reader.metaFromRemote) {
8749                 p._requestMeta = 1;
8750             }
8751             if(this.sortInfo && this.remoteSort){
8752                 var pn = this.paramNames;
8753                 p[pn["sort"]] = this.sortInfo.field;
8754                 p[pn["dir"]] = this.sortInfo.direction;
8755             }
8756             if (this.multiSort) {
8757                 var pn = this.paramNames;
8758                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
8759             }
8760             
8761             this.proxy.load(p, this.reader, this.loadRecords, this, options);
8762         }
8763     },
8764
8765     /**
8766      * Reloads the Record cache from the configured Proxy using the configured Reader and
8767      * the options from the last load operation performed.
8768      * @param {Object} options (optional) An object containing properties which may override the options
8769      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
8770      * the most recently used options are reused).
8771      */
8772     reload : function(options){
8773         this.load(Roo.applyIf(options||{}, this.lastOptions));
8774     },
8775
8776     // private
8777     // Called as a callback by the Reader during a load operation.
8778     loadRecords : function(o, options, success){
8779         if(!o || success === false){
8780             if(success !== false){
8781                 this.fireEvent("load", this, [], options, o);
8782             }
8783             if(options.callback){
8784                 options.callback.call(options.scope || this, [], options, false);
8785             }
8786             return;
8787         }
8788         // if data returned failure - throw an exception.
8789         if (o.success === false) {
8790             // show a message if no listener is registered.
8791             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
8792                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
8793             }
8794             // loadmask wil be hooked into this..
8795             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
8796             return;
8797         }
8798         var r = o.records, t = o.totalRecords || r.length;
8799         
8800         this.fireEvent("beforeloadadd", this, r, options, o);
8801         
8802         if(!options || options.add !== true){
8803             if(this.pruneModifiedRecords){
8804                 this.modified = [];
8805             }
8806             for(var i = 0, len = r.length; i < len; i++){
8807                 r[i].join(this);
8808             }
8809             if(this.snapshot){
8810                 this.data = this.snapshot;
8811                 delete this.snapshot;
8812             }
8813             this.data.clear();
8814             this.data.addAll(r);
8815             this.totalLength = t;
8816             this.applySort();
8817             this.fireEvent("datachanged", this);
8818         }else{
8819             this.totalLength = Math.max(t, this.data.length+r.length);
8820             this.add(r);
8821         }
8822         this.fireEvent("load", this, r, options, o);
8823         if(options.callback){
8824             options.callback.call(options.scope || this, r, options, true);
8825         }
8826     },
8827
8828
8829     /**
8830      * Loads data from a passed data block. A Reader which understands the format of the data
8831      * must have been configured in the constructor.
8832      * @param {Object} data The data block from which to read the Records.  The format of the data expected
8833      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
8834      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
8835      */
8836     loadData : function(o, append){
8837         var r = this.reader.readRecords(o);
8838         this.loadRecords(r, {add: append}, true);
8839     },
8840
8841     /**
8842      * Gets the number of cached records.
8843      * <p>
8844      * <em>If using paging, this may not be the total size of the dataset. If the data object
8845      * used by the Reader contains the dataset size, then the getTotalCount() function returns
8846      * the data set size</em>
8847      */
8848     getCount : function(){
8849         return this.data.length || 0;
8850     },
8851
8852     /**
8853      * Gets the total number of records in the dataset as returned by the server.
8854      * <p>
8855      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
8856      * the dataset size</em>
8857      */
8858     getTotalCount : function(){
8859         return this.totalLength || 0;
8860     },
8861
8862     /**
8863      * Returns the sort state of the Store as an object with two properties:
8864      * <pre><code>
8865  field {String} The name of the field by which the Records are sorted
8866  direction {String} The sort order, "ASC" or "DESC"
8867      * </code></pre>
8868      */
8869     getSortState : function(){
8870         return this.sortInfo;
8871     },
8872
8873     // private
8874     applySort : function(){
8875         if(this.sortInfo && !this.remoteSort){
8876             var s = this.sortInfo, f = s.field;
8877             var st = this.fields.get(f).sortType;
8878             var fn = function(r1, r2){
8879                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
8880                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
8881             };
8882             this.data.sort(s.direction, fn);
8883             if(this.snapshot && this.snapshot != this.data){
8884                 this.snapshot.sort(s.direction, fn);
8885             }
8886         }
8887     },
8888
8889     /**
8890      * Sets the default sort column and order to be used by the next load operation.
8891      * @param {String} fieldName The name of the field to sort by.
8892      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
8893      */
8894     setDefaultSort : function(field, dir){
8895         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
8896     },
8897
8898     /**
8899      * Sort the Records.
8900      * If remote sorting is used, the sort is performed on the server, and the cache is
8901      * reloaded. If local sorting is used, the cache is sorted internally.
8902      * @param {String} fieldName The name of the field to sort by.
8903      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
8904      */
8905     sort : function(fieldName, dir){
8906         var f = this.fields.get(fieldName);
8907         if(!dir){
8908             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
8909             
8910             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
8911                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
8912             }else{
8913                 dir = f.sortDir;
8914             }
8915         }
8916         this.sortToggle[f.name] = dir;
8917         this.sortInfo = {field: f.name, direction: dir};
8918         if(!this.remoteSort){
8919             this.applySort();
8920             this.fireEvent("datachanged", this);
8921         }else{
8922             this.load(this.lastOptions);
8923         }
8924     },
8925
8926     /**
8927      * Calls the specified function for each of the Records in the cache.
8928      * @param {Function} fn The function to call. The Record is passed as the first parameter.
8929      * Returning <em>false</em> aborts and exits the iteration.
8930      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
8931      */
8932     each : function(fn, scope){
8933         this.data.each(fn, scope);
8934     },
8935
8936     /**
8937      * Gets all records modified since the last commit.  Modified records are persisted across load operations
8938      * (e.g., during paging).
8939      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
8940      */
8941     getModifiedRecords : function(){
8942         return this.modified;
8943     },
8944
8945     // private
8946     createFilterFn : function(property, value, anyMatch){
8947         if(!value.exec){ // not a regex
8948             value = String(value);
8949             if(value.length == 0){
8950                 return false;
8951             }
8952             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
8953         }
8954         return function(r){
8955             return value.test(r.data[property]);
8956         };
8957     },
8958
8959     /**
8960      * Sums the value of <i>property</i> for each record between start and end and returns the result.
8961      * @param {String} property A field on your records
8962      * @param {Number} start The record index to start at (defaults to 0)
8963      * @param {Number} end The last record index to include (defaults to length - 1)
8964      * @return {Number} The sum
8965      */
8966     sum : function(property, start, end){
8967         var rs = this.data.items, v = 0;
8968         start = start || 0;
8969         end = (end || end === 0) ? end : rs.length-1;
8970
8971         for(var i = start; i <= end; i++){
8972             v += (rs[i].data[property] || 0);
8973         }
8974         return v;
8975     },
8976
8977     /**
8978      * Filter the records by a specified property.
8979      * @param {String} field A field on your records
8980      * @param {String/RegExp} value Either a string that the field
8981      * should start with or a RegExp to test against the field
8982      * @param {Boolean} anyMatch True to match any part not just the beginning
8983      */
8984     filter : function(property, value, anyMatch){
8985         var fn = this.createFilterFn(property, value, anyMatch);
8986         return fn ? this.filterBy(fn) : this.clearFilter();
8987     },
8988
8989     /**
8990      * Filter by a function. The specified function will be called with each
8991      * record in this data source. If the function returns true the record is included,
8992      * otherwise it is filtered.
8993      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
8994      * @param {Object} scope (optional) The scope of the function (defaults to this)
8995      */
8996     filterBy : function(fn, scope){
8997         this.snapshot = this.snapshot || this.data;
8998         this.data = this.queryBy(fn, scope||this);
8999         this.fireEvent("datachanged", this);
9000     },
9001
9002     /**
9003      * Query the records by a specified property.
9004      * @param {String} field A field on your records
9005      * @param {String/RegExp} value Either a string that the field
9006      * should start with or a RegExp to test against the field
9007      * @param {Boolean} anyMatch True to match any part not just the beginning
9008      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9009      */
9010     query : function(property, value, anyMatch){
9011         var fn = this.createFilterFn(property, value, anyMatch);
9012         return fn ? this.queryBy(fn) : this.data.clone();
9013     },
9014
9015     /**
9016      * Query by a function. The specified function will be called with each
9017      * record in this data source. If the function returns true the record is included
9018      * in the results.
9019      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9020      * @param {Object} scope (optional) The scope of the function (defaults to this)
9021       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9022      **/
9023     queryBy : function(fn, scope){
9024         var data = this.snapshot || this.data;
9025         return data.filterBy(fn, scope||this);
9026     },
9027
9028     /**
9029      * Collects unique values for a particular dataIndex from this store.
9030      * @param {String} dataIndex The property to collect
9031      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
9032      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
9033      * @return {Array} An array of the unique values
9034      **/
9035     collect : function(dataIndex, allowNull, bypassFilter){
9036         var d = (bypassFilter === true && this.snapshot) ?
9037                 this.snapshot.items : this.data.items;
9038         var v, sv, r = [], l = {};
9039         for(var i = 0, len = d.length; i < len; i++){
9040             v = d[i].data[dataIndex];
9041             sv = String(v);
9042             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
9043                 l[sv] = true;
9044                 r[r.length] = v;
9045             }
9046         }
9047         return r;
9048     },
9049
9050     /**
9051      * Revert to a view of the Record cache with no filtering applied.
9052      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
9053      */
9054     clearFilter : function(suppressEvent){
9055         if(this.snapshot && this.snapshot != this.data){
9056             this.data = this.snapshot;
9057             delete this.snapshot;
9058             if(suppressEvent !== true){
9059                 this.fireEvent("datachanged", this);
9060             }
9061         }
9062     },
9063
9064     // private
9065     afterEdit : function(record){
9066         if(this.modified.indexOf(record) == -1){
9067             this.modified.push(record);
9068         }
9069         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
9070     },
9071     
9072     // private
9073     afterReject : function(record){
9074         this.modified.remove(record);
9075         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
9076     },
9077
9078     // private
9079     afterCommit : function(record){
9080         this.modified.remove(record);
9081         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
9082     },
9083
9084     /**
9085      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
9086      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
9087      */
9088     commitChanges : function(){
9089         var m = this.modified.slice(0);
9090         this.modified = [];
9091         for(var i = 0, len = m.length; i < len; i++){
9092             m[i].commit();
9093         }
9094     },
9095
9096     /**
9097      * Cancel outstanding changes on all changed records.
9098      */
9099     rejectChanges : function(){
9100         var m = this.modified.slice(0);
9101         this.modified = [];
9102         for(var i = 0, len = m.length; i < len; i++){
9103             m[i].reject();
9104         }
9105     },
9106
9107     onMetaChange : function(meta, rtype, o){
9108         this.recordType = rtype;
9109         this.fields = rtype.prototype.fields;
9110         delete this.snapshot;
9111         this.sortInfo = meta.sortInfo || this.sortInfo;
9112         this.modified = [];
9113         this.fireEvent('metachange', this, this.reader.meta);
9114     },
9115     
9116     moveIndex : function(data, type)
9117     {
9118         var index = this.indexOf(data);
9119         
9120         var newIndex = index + type;
9121         
9122         this.remove(data);
9123         
9124         this.insert(newIndex, data);
9125         
9126     }
9127 });/*
9128  * Based on:
9129  * Ext JS Library 1.1.1
9130  * Copyright(c) 2006-2007, Ext JS, LLC.
9131  *
9132  * Originally Released Under LGPL - original licence link has changed is not relivant.
9133  *
9134  * Fork - LGPL
9135  * <script type="text/javascript">
9136  */
9137
9138 /**
9139  * @class Roo.data.SimpleStore
9140  * @extends Roo.data.Store
9141  * Small helper class to make creating Stores from Array data easier.
9142  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
9143  * @cfg {Array} fields An array of field definition objects, or field name strings.
9144  * @cfg {Array} data The multi-dimensional array of data
9145  * @constructor
9146  * @param {Object} config
9147  */
9148 Roo.data.SimpleStore = function(config){
9149     Roo.data.SimpleStore.superclass.constructor.call(this, {
9150         isLocal : true,
9151         reader: new Roo.data.ArrayReader({
9152                 id: config.id
9153             },
9154             Roo.data.Record.create(config.fields)
9155         ),
9156         proxy : new Roo.data.MemoryProxy(config.data)
9157     });
9158     this.load();
9159 };
9160 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
9161  * Based on:
9162  * Ext JS Library 1.1.1
9163  * Copyright(c) 2006-2007, Ext JS, LLC.
9164  *
9165  * Originally Released Under LGPL - original licence link has changed is not relivant.
9166  *
9167  * Fork - LGPL
9168  * <script type="text/javascript">
9169  */
9170
9171 /**
9172 /**
9173  * @extends Roo.data.Store
9174  * @class Roo.data.JsonStore
9175  * Small helper class to make creating Stores for JSON data easier. <br/>
9176 <pre><code>
9177 var store = new Roo.data.JsonStore({
9178     url: 'get-images.php',
9179     root: 'images',
9180     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
9181 });
9182 </code></pre>
9183  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
9184  * JsonReader and HttpProxy (unless inline data is provided).</b>
9185  * @cfg {Array} fields An array of field definition objects, or field name strings.
9186  * @constructor
9187  * @param {Object} config
9188  */
9189 Roo.data.JsonStore = function(c){
9190     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
9191         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
9192         reader: new Roo.data.JsonReader(c, c.fields)
9193     }));
9194 };
9195 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
9196  * Based on:
9197  * Ext JS Library 1.1.1
9198  * Copyright(c) 2006-2007, Ext JS, LLC.
9199  *
9200  * Originally Released Under LGPL - original licence link has changed is not relivant.
9201  *
9202  * Fork - LGPL
9203  * <script type="text/javascript">
9204  */
9205
9206  
9207 Roo.data.Field = function(config){
9208     if(typeof config == "string"){
9209         config = {name: config};
9210     }
9211     Roo.apply(this, config);
9212     
9213     if(!this.type){
9214         this.type = "auto";
9215     }
9216     
9217     var st = Roo.data.SortTypes;
9218     // named sortTypes are supported, here we look them up
9219     if(typeof this.sortType == "string"){
9220         this.sortType = st[this.sortType];
9221     }
9222     
9223     // set default sortType for strings and dates
9224     if(!this.sortType){
9225         switch(this.type){
9226             case "string":
9227                 this.sortType = st.asUCString;
9228                 break;
9229             case "date":
9230                 this.sortType = st.asDate;
9231                 break;
9232             default:
9233                 this.sortType = st.none;
9234         }
9235     }
9236
9237     // define once
9238     var stripRe = /[\$,%]/g;
9239
9240     // prebuilt conversion function for this field, instead of
9241     // switching every time we're reading a value
9242     if(!this.convert){
9243         var cv, dateFormat = this.dateFormat;
9244         switch(this.type){
9245             case "":
9246             case "auto":
9247             case undefined:
9248                 cv = function(v){ return v; };
9249                 break;
9250             case "string":
9251                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
9252                 break;
9253             case "int":
9254                 cv = function(v){
9255                     return v !== undefined && v !== null && v !== '' ?
9256                            parseInt(String(v).replace(stripRe, ""), 10) : '';
9257                     };
9258                 break;
9259             case "float":
9260                 cv = function(v){
9261                     return v !== undefined && v !== null && v !== '' ?
9262                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
9263                     };
9264                 break;
9265             case "bool":
9266             case "boolean":
9267                 cv = function(v){ return v === true || v === "true" || v == 1; };
9268                 break;
9269             case "date":
9270                 cv = function(v){
9271                     if(!v){
9272                         return '';
9273                     }
9274                     if(v instanceof Date){
9275                         return v;
9276                     }
9277                     if(dateFormat){
9278                         if(dateFormat == "timestamp"){
9279                             return new Date(v*1000);
9280                         }
9281                         return Date.parseDate(v, dateFormat);
9282                     }
9283                     var parsed = Date.parse(v);
9284                     return parsed ? new Date(parsed) : null;
9285                 };
9286              break;
9287             
9288         }
9289         this.convert = cv;
9290     }
9291 };
9292
9293 Roo.data.Field.prototype = {
9294     dateFormat: null,
9295     defaultValue: "",
9296     mapping: null,
9297     sortType : null,
9298     sortDir : "ASC"
9299 };/*
9300  * Based on:
9301  * Ext JS Library 1.1.1
9302  * Copyright(c) 2006-2007, Ext JS, LLC.
9303  *
9304  * Originally Released Under LGPL - original licence link has changed is not relivant.
9305  *
9306  * Fork - LGPL
9307  * <script type="text/javascript">
9308  */
9309  
9310 // Base class for reading structured data from a data source.  This class is intended to be
9311 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
9312
9313 /**
9314  * @class Roo.data.DataReader
9315  * Base class for reading structured data from a data source.  This class is intended to be
9316  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
9317  */
9318
9319 Roo.data.DataReader = function(meta, recordType){
9320     
9321     this.meta = meta;
9322     
9323     this.recordType = recordType instanceof Array ? 
9324         Roo.data.Record.create(recordType) : recordType;
9325 };
9326
9327 Roo.data.DataReader.prototype = {
9328      /**
9329      * Create an empty record
9330      * @param {Object} data (optional) - overlay some values
9331      * @return {Roo.data.Record} record created.
9332      */
9333     newRow :  function(d) {
9334         var da =  {};
9335         this.recordType.prototype.fields.each(function(c) {
9336             switch( c.type) {
9337                 case 'int' : da[c.name] = 0; break;
9338                 case 'date' : da[c.name] = new Date(); break;
9339                 case 'float' : da[c.name] = 0.0; break;
9340                 case 'boolean' : da[c.name] = false; break;
9341                 default : da[c.name] = ""; break;
9342             }
9343             
9344         });
9345         return new this.recordType(Roo.apply(da, d));
9346     }
9347     
9348 };/*
9349  * Based on:
9350  * Ext JS Library 1.1.1
9351  * Copyright(c) 2006-2007, Ext JS, LLC.
9352  *
9353  * Originally Released Under LGPL - original licence link has changed is not relivant.
9354  *
9355  * Fork - LGPL
9356  * <script type="text/javascript">
9357  */
9358
9359 /**
9360  * @class Roo.data.DataProxy
9361  * @extends Roo.data.Observable
9362  * This class is an abstract base class for implementations which provide retrieval of
9363  * unformatted data objects.<br>
9364  * <p>
9365  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
9366  * (of the appropriate type which knows how to parse the data object) to provide a block of
9367  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
9368  * <p>
9369  * Custom implementations must implement the load method as described in
9370  * {@link Roo.data.HttpProxy#load}.
9371  */
9372 Roo.data.DataProxy = function(){
9373     this.addEvents({
9374         /**
9375          * @event beforeload
9376          * Fires before a network request is made to retrieve a data object.
9377          * @param {Object} This DataProxy object.
9378          * @param {Object} params The params parameter to the load function.
9379          */
9380         beforeload : true,
9381         /**
9382          * @event load
9383          * Fires before the load method's callback is called.
9384          * @param {Object} This DataProxy object.
9385          * @param {Object} o The data object.
9386          * @param {Object} arg The callback argument object passed to the load function.
9387          */
9388         load : true,
9389         /**
9390          * @event loadexception
9391          * Fires if an Exception occurs during data retrieval.
9392          * @param {Object} This DataProxy object.
9393          * @param {Object} o The data object.
9394          * @param {Object} arg The callback argument object passed to the load function.
9395          * @param {Object} e The Exception.
9396          */
9397         loadexception : true
9398     });
9399     Roo.data.DataProxy.superclass.constructor.call(this);
9400 };
9401
9402 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
9403
9404     /**
9405      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
9406      */
9407 /*
9408  * Based on:
9409  * Ext JS Library 1.1.1
9410  * Copyright(c) 2006-2007, Ext JS, LLC.
9411  *
9412  * Originally Released Under LGPL - original licence link has changed is not relivant.
9413  *
9414  * Fork - LGPL
9415  * <script type="text/javascript">
9416  */
9417 /**
9418  * @class Roo.data.MemoryProxy
9419  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
9420  * to the Reader when its load method is called.
9421  * @constructor
9422  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
9423  */
9424 Roo.data.MemoryProxy = function(data){
9425     if (data.data) {
9426         data = data.data;
9427     }
9428     Roo.data.MemoryProxy.superclass.constructor.call(this);
9429     this.data = data;
9430 };
9431
9432 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
9433     /**
9434      * Load data from the requested source (in this case an in-memory
9435      * data object passed to the constructor), read the data object into
9436      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9437      * process that block using the passed callback.
9438      * @param {Object} params This parameter is not used by the MemoryProxy class.
9439      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9440      * object into a block of Roo.data.Records.
9441      * @param {Function} callback The function into which to pass the block of Roo.data.records.
9442      * The function must be passed <ul>
9443      * <li>The Record block object</li>
9444      * <li>The "arg" argument from the load function</li>
9445      * <li>A boolean success indicator</li>
9446      * </ul>
9447      * @param {Object} scope The scope in which to call the callback
9448      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9449      */
9450     load : function(params, reader, callback, scope, arg){
9451         params = params || {};
9452         var result;
9453         try {
9454             result = reader.readRecords(this.data);
9455         }catch(e){
9456             this.fireEvent("loadexception", this, arg, null, e);
9457             callback.call(scope, null, arg, false);
9458             return;
9459         }
9460         callback.call(scope, result, arg, true);
9461     },
9462     
9463     // private
9464     update : function(params, records){
9465         
9466     }
9467 });/*
9468  * Based on:
9469  * Ext JS Library 1.1.1
9470  * Copyright(c) 2006-2007, Ext JS, LLC.
9471  *
9472  * Originally Released Under LGPL - original licence link has changed is not relivant.
9473  *
9474  * Fork - LGPL
9475  * <script type="text/javascript">
9476  */
9477 /**
9478  * @class Roo.data.HttpProxy
9479  * @extends Roo.data.DataProxy
9480  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
9481  * configured to reference a certain URL.<br><br>
9482  * <p>
9483  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
9484  * from which the running page was served.<br><br>
9485  * <p>
9486  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
9487  * <p>
9488  * Be aware that to enable the browser to parse an XML document, the server must set
9489  * the Content-Type header in the HTTP response to "text/xml".
9490  * @constructor
9491  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
9492  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
9493  * will be used to make the request.
9494  */
9495 Roo.data.HttpProxy = function(conn){
9496     Roo.data.HttpProxy.superclass.constructor.call(this);
9497     // is conn a conn config or a real conn?
9498     this.conn = conn;
9499     this.useAjax = !conn || !conn.events;
9500   
9501 };
9502
9503 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
9504     // thse are take from connection...
9505     
9506     /**
9507      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
9508      */
9509     /**
9510      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
9511      * extra parameters to each request made by this object. (defaults to undefined)
9512      */
9513     /**
9514      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
9515      *  to each request made by this object. (defaults to undefined)
9516      */
9517     /**
9518      * @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)
9519      */
9520     /**
9521      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
9522      */
9523      /**
9524      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
9525      * @type Boolean
9526      */
9527   
9528
9529     /**
9530      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
9531      * @type Boolean
9532      */
9533     /**
9534      * Return the {@link Roo.data.Connection} object being used by this Proxy.
9535      * @return {Connection} The Connection object. This object may be used to subscribe to events on
9536      * a finer-grained basis than the DataProxy events.
9537      */
9538     getConnection : function(){
9539         return this.useAjax ? Roo.Ajax : this.conn;
9540     },
9541
9542     /**
9543      * Load data from the configured {@link Roo.data.Connection}, read the data object into
9544      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
9545      * process that block using the passed callback.
9546      * @param {Object} params An object containing properties which are to be used as HTTP parameters
9547      * for the request to the remote server.
9548      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9549      * object into a block of Roo.data.Records.
9550      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9551      * The function must be passed <ul>
9552      * <li>The Record block object</li>
9553      * <li>The "arg" argument from the load function</li>
9554      * <li>A boolean success indicator</li>
9555      * </ul>
9556      * @param {Object} scope The scope in which to call the callback
9557      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9558      */
9559     load : function(params, reader, callback, scope, arg){
9560         if(this.fireEvent("beforeload", this, params) !== false){
9561             var  o = {
9562                 params : params || {},
9563                 request: {
9564                     callback : callback,
9565                     scope : scope,
9566                     arg : arg
9567                 },
9568                 reader: reader,
9569                 callback : this.loadResponse,
9570                 scope: this
9571             };
9572             if(this.useAjax){
9573                 Roo.applyIf(o, this.conn);
9574                 if(this.activeRequest){
9575                     Roo.Ajax.abort(this.activeRequest);
9576                 }
9577                 this.activeRequest = Roo.Ajax.request(o);
9578             }else{
9579                 this.conn.request(o);
9580             }
9581         }else{
9582             callback.call(scope||this, null, arg, false);
9583         }
9584     },
9585
9586     // private
9587     loadResponse : function(o, success, response){
9588         delete this.activeRequest;
9589         if(!success){
9590             this.fireEvent("loadexception", this, o, response);
9591             o.request.callback.call(o.request.scope, null, o.request.arg, false);
9592             return;
9593         }
9594         var result;
9595         try {
9596             result = o.reader.read(response);
9597         }catch(e){
9598             this.fireEvent("loadexception", this, o, response, e);
9599             o.request.callback.call(o.request.scope, null, o.request.arg, false);
9600             return;
9601         }
9602         
9603         this.fireEvent("load", this, o, o.request.arg);
9604         o.request.callback.call(o.request.scope, result, o.request.arg, true);
9605     },
9606
9607     // private
9608     update : function(dataSet){
9609
9610     },
9611
9612     // private
9613     updateResponse : function(dataSet){
9614
9615     }
9616 });/*
9617  * Based on:
9618  * Ext JS Library 1.1.1
9619  * Copyright(c) 2006-2007, Ext JS, LLC.
9620  *
9621  * Originally Released Under LGPL - original licence link has changed is not relivant.
9622  *
9623  * Fork - LGPL
9624  * <script type="text/javascript">
9625  */
9626
9627 /**
9628  * @class Roo.data.ScriptTagProxy
9629  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
9630  * other than the originating domain of the running page.<br><br>
9631  * <p>
9632  * <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
9633  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
9634  * <p>
9635  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
9636  * source code that is used as the source inside a &lt;script> tag.<br><br>
9637  * <p>
9638  * In order for the browser to process the returned data, the server must wrap the data object
9639  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
9640  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
9641  * depending on whether the callback name was passed:
9642  * <p>
9643  * <pre><code>
9644 boolean scriptTag = false;
9645 String cb = request.getParameter("callback");
9646 if (cb != null) {
9647     scriptTag = true;
9648     response.setContentType("text/javascript");
9649 } else {
9650     response.setContentType("application/x-json");
9651 }
9652 Writer out = response.getWriter();
9653 if (scriptTag) {
9654     out.write(cb + "(");
9655 }
9656 out.print(dataBlock.toJsonString());
9657 if (scriptTag) {
9658     out.write(");");
9659 }
9660 </pre></code>
9661  *
9662  * @constructor
9663  * @param {Object} config A configuration object.
9664  */
9665 Roo.data.ScriptTagProxy = function(config){
9666     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
9667     Roo.apply(this, config);
9668     this.head = document.getElementsByTagName("head")[0];
9669 };
9670
9671 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
9672
9673 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
9674     /**
9675      * @cfg {String} url The URL from which to request the data object.
9676      */
9677     /**
9678      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
9679      */
9680     timeout : 30000,
9681     /**
9682      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
9683      * the server the name of the callback function set up by the load call to process the returned data object.
9684      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
9685      * javascript output which calls this named function passing the data object as its only parameter.
9686      */
9687     callbackParam : "callback",
9688     /**
9689      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
9690      * name to the request.
9691      */
9692     nocache : true,
9693
9694     /**
9695      * Load data from the configured URL, read the data object into
9696      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9697      * process that block using the passed callback.
9698      * @param {Object} params An object containing properties which are to be used as HTTP parameters
9699      * for the request to the remote server.
9700      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9701      * object into a block of Roo.data.Records.
9702      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9703      * The function must be passed <ul>
9704      * <li>The Record block object</li>
9705      * <li>The "arg" argument from the load function</li>
9706      * <li>A boolean success indicator</li>
9707      * </ul>
9708      * @param {Object} scope The scope in which to call the callback
9709      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9710      */
9711     load : function(params, reader, callback, scope, arg){
9712         if(this.fireEvent("beforeload", this, params) !== false){
9713
9714             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
9715
9716             var url = this.url;
9717             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
9718             if(this.nocache){
9719                 url += "&_dc=" + (new Date().getTime());
9720             }
9721             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
9722             var trans = {
9723                 id : transId,
9724                 cb : "stcCallback"+transId,
9725                 scriptId : "stcScript"+transId,
9726                 params : params,
9727                 arg : arg,
9728                 url : url,
9729                 callback : callback,
9730                 scope : scope,
9731                 reader : reader
9732             };
9733             var conn = this;
9734
9735             window[trans.cb] = function(o){
9736                 conn.handleResponse(o, trans);
9737             };
9738
9739             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
9740
9741             if(this.autoAbort !== false){
9742                 this.abort();
9743             }
9744
9745             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
9746
9747             var script = document.createElement("script");
9748             script.setAttribute("src", url);
9749             script.setAttribute("type", "text/javascript");
9750             script.setAttribute("id", trans.scriptId);
9751             this.head.appendChild(script);
9752
9753             this.trans = trans;
9754         }else{
9755             callback.call(scope||this, null, arg, false);
9756         }
9757     },
9758
9759     // private
9760     isLoading : function(){
9761         return this.trans ? true : false;
9762     },
9763
9764     /**
9765      * Abort the current server request.
9766      */
9767     abort : function(){
9768         if(this.isLoading()){
9769             this.destroyTrans(this.trans);
9770         }
9771     },
9772
9773     // private
9774     destroyTrans : function(trans, isLoaded){
9775         this.head.removeChild(document.getElementById(trans.scriptId));
9776         clearTimeout(trans.timeoutId);
9777         if(isLoaded){
9778             window[trans.cb] = undefined;
9779             try{
9780                 delete window[trans.cb];
9781             }catch(e){}
9782         }else{
9783             // if hasn't been loaded, wait for load to remove it to prevent script error
9784             window[trans.cb] = function(){
9785                 window[trans.cb] = undefined;
9786                 try{
9787                     delete window[trans.cb];
9788                 }catch(e){}
9789             };
9790         }
9791     },
9792
9793     // private
9794     handleResponse : function(o, trans){
9795         this.trans = false;
9796         this.destroyTrans(trans, true);
9797         var result;
9798         try {
9799             result = trans.reader.readRecords(o);
9800         }catch(e){
9801             this.fireEvent("loadexception", this, o, trans.arg, e);
9802             trans.callback.call(trans.scope||window, null, trans.arg, false);
9803             return;
9804         }
9805         this.fireEvent("load", this, o, trans.arg);
9806         trans.callback.call(trans.scope||window, result, trans.arg, true);
9807     },
9808
9809     // private
9810     handleFailure : function(trans){
9811         this.trans = false;
9812         this.destroyTrans(trans, false);
9813         this.fireEvent("loadexception", this, null, trans.arg);
9814         trans.callback.call(trans.scope||window, null, trans.arg, false);
9815     }
9816 });/*
9817  * Based on:
9818  * Ext JS Library 1.1.1
9819  * Copyright(c) 2006-2007, Ext JS, LLC.
9820  *
9821  * Originally Released Under LGPL - original licence link has changed is not relivant.
9822  *
9823  * Fork - LGPL
9824  * <script type="text/javascript">
9825  */
9826
9827 /**
9828  * @class Roo.data.JsonReader
9829  * @extends Roo.data.DataReader
9830  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
9831  * based on mappings in a provided Roo.data.Record constructor.
9832  * 
9833  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
9834  * in the reply previously. 
9835  * 
9836  * <p>
9837  * Example code:
9838  * <pre><code>
9839 var RecordDef = Roo.data.Record.create([
9840     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
9841     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
9842 ]);
9843 var myReader = new Roo.data.JsonReader({
9844     totalProperty: "results",    // The property which contains the total dataset size (optional)
9845     root: "rows",                // The property which contains an Array of row objects
9846     id: "id"                     // The property within each row object that provides an ID for the record (optional)
9847 }, RecordDef);
9848 </code></pre>
9849  * <p>
9850  * This would consume a JSON file like this:
9851  * <pre><code>
9852 { 'results': 2, 'rows': [
9853     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
9854     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
9855 }
9856 </code></pre>
9857  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
9858  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
9859  * paged from the remote server.
9860  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
9861  * @cfg {String} root name of the property which contains the Array of row objects.
9862  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
9863  * @constructor
9864  * Create a new JsonReader
9865  * @param {Object} meta Metadata configuration options
9866  * @param {Object} recordType Either an Array of field definition objects,
9867  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
9868  */
9869 Roo.data.JsonReader = function(meta, recordType){
9870     
9871     meta = meta || {};
9872     // set some defaults:
9873     Roo.applyIf(meta, {
9874         totalProperty: 'total',
9875         successProperty : 'success',
9876         root : 'data',
9877         id : 'id'
9878     });
9879     
9880     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
9881 };
9882 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
9883     
9884     /**
9885      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
9886      * Used by Store query builder to append _requestMeta to params.
9887      * 
9888      */
9889     metaFromRemote : false,
9890     /**
9891      * This method is only used by a DataProxy which has retrieved data from a remote server.
9892      * @param {Object} response The XHR object which contains the JSON data in its responseText.
9893      * @return {Object} data A data block which is used by an Roo.data.Store object as
9894      * a cache of Roo.data.Records.
9895      */
9896     read : function(response){
9897         var json = response.responseText;
9898        
9899         var o = /* eval:var:o */ eval("("+json+")");
9900         if(!o) {
9901             throw {message: "JsonReader.read: Json object not found"};
9902         }
9903         
9904         if(o.metaData){
9905             
9906             delete this.ef;
9907             this.metaFromRemote = true;
9908             this.meta = o.metaData;
9909             this.recordType = Roo.data.Record.create(o.metaData.fields);
9910             this.onMetaChange(this.meta, this.recordType, o);
9911         }
9912         return this.readRecords(o);
9913     },
9914
9915     // private function a store will implement
9916     onMetaChange : function(meta, recordType, o){
9917
9918     },
9919
9920     /**
9921          * @ignore
9922          */
9923     simpleAccess: function(obj, subsc) {
9924         return obj[subsc];
9925     },
9926
9927         /**
9928          * @ignore
9929          */
9930     getJsonAccessor: function(){
9931         var re = /[\[\.]/;
9932         return function(expr) {
9933             try {
9934                 return(re.test(expr))
9935                     ? new Function("obj", "return obj." + expr)
9936                     : function(obj){
9937                         return obj[expr];
9938                     };
9939             } catch(e){}
9940             return Roo.emptyFn;
9941         };
9942     }(),
9943
9944     /**
9945      * Create a data block containing Roo.data.Records from an XML document.
9946      * @param {Object} o An object which contains an Array of row objects in the property specified
9947      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
9948      * which contains the total size of the dataset.
9949      * @return {Object} data A data block which is used by an Roo.data.Store object as
9950      * a cache of Roo.data.Records.
9951      */
9952     readRecords : function(o){
9953         /**
9954          * After any data loads, the raw JSON data is available for further custom processing.
9955          * @type Object
9956          */
9957         this.o = o;
9958         var s = this.meta, Record = this.recordType,
9959             f = Record.prototype.fields, fi = f.items, fl = f.length;
9960
9961 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
9962         if (!this.ef) {
9963             if(s.totalProperty) {
9964                     this.getTotal = this.getJsonAccessor(s.totalProperty);
9965                 }
9966                 if(s.successProperty) {
9967                     this.getSuccess = this.getJsonAccessor(s.successProperty);
9968                 }
9969                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
9970                 if (s.id) {
9971                         var g = this.getJsonAccessor(s.id);
9972                         this.getId = function(rec) {
9973                                 var r = g(rec);
9974                                 return (r === undefined || r === "") ? null : r;
9975                         };
9976                 } else {
9977                         this.getId = function(){return null;};
9978                 }
9979             this.ef = [];
9980             for(var jj = 0; jj < fl; jj++){
9981                 f = fi[jj];
9982                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
9983                 this.ef[jj] = this.getJsonAccessor(map);
9984             }
9985         }
9986
9987         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
9988         if(s.totalProperty){
9989             var vt = parseInt(this.getTotal(o), 10);
9990             if(!isNaN(vt)){
9991                 totalRecords = vt;
9992             }
9993         }
9994         if(s.successProperty){
9995             var vs = this.getSuccess(o);
9996             if(vs === false || vs === 'false'){
9997                 success = false;
9998             }
9999         }
10000         var records = [];
10001             for(var i = 0; i < c; i++){
10002                     var n = root[i];
10003                 var values = {};
10004                 var id = this.getId(n);
10005                 for(var j = 0; j < fl; j++){
10006                     f = fi[j];
10007                 var v = this.ef[j](n);
10008                 if (!f.convert) {
10009                     Roo.log('missing convert for ' + f.name);
10010                     Roo.log(f);
10011                     continue;
10012                 }
10013                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
10014                 }
10015                 var record = new Record(values, id);
10016                 record.json = n;
10017                 records[i] = record;
10018             }
10019             return {
10020             raw : o,
10021                 success : success,
10022                 records : records,
10023                 totalRecords : totalRecords
10024             };
10025     }
10026 });/*
10027  * Based on:
10028  * Ext JS Library 1.1.1
10029  * Copyright(c) 2006-2007, Ext JS, LLC.
10030  *
10031  * Originally Released Under LGPL - original licence link has changed is not relivant.
10032  *
10033  * Fork - LGPL
10034  * <script type="text/javascript">
10035  */
10036
10037 /**
10038  * @class Roo.data.ArrayReader
10039  * @extends Roo.data.DataReader
10040  * Data reader class to create an Array of Roo.data.Record objects from an Array.
10041  * Each element of that Array represents a row of data fields. The
10042  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
10043  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
10044  * <p>
10045  * Example code:.
10046  * <pre><code>
10047 var RecordDef = Roo.data.Record.create([
10048     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
10049     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
10050 ]);
10051 var myReader = new Roo.data.ArrayReader({
10052     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
10053 }, RecordDef);
10054 </code></pre>
10055  * <p>
10056  * This would consume an Array like this:
10057  * <pre><code>
10058 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
10059   </code></pre>
10060  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
10061  * @constructor
10062  * Create a new JsonReader
10063  * @param {Object} meta Metadata configuration options.
10064  * @param {Object} recordType Either an Array of field definition objects
10065  * as specified to {@link Roo.data.Record#create},
10066  * or an {@link Roo.data.Record} object
10067  * created using {@link Roo.data.Record#create}.
10068  */
10069 Roo.data.ArrayReader = function(meta, recordType){
10070     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
10071 };
10072
10073 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
10074     /**
10075      * Create a data block containing Roo.data.Records from an XML document.
10076      * @param {Object} o An Array of row objects which represents the dataset.
10077      * @return {Object} data A data block which is used by an Roo.data.Store object as
10078      * a cache of Roo.data.Records.
10079      */
10080     readRecords : function(o){
10081         var sid = this.meta ? this.meta.id : null;
10082         var recordType = this.recordType, fields = recordType.prototype.fields;
10083         var records = [];
10084         var root = o;
10085             for(var i = 0; i < root.length; i++){
10086                     var n = root[i];
10087                 var values = {};
10088                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
10089                 for(var j = 0, jlen = fields.length; j < jlen; j++){
10090                 var f = fields.items[j];
10091                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
10092                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
10093                 v = f.convert(v);
10094                 values[f.name] = v;
10095             }
10096                 var record = new recordType(values, id);
10097                 record.json = n;
10098                 records[records.length] = record;
10099             }
10100             return {
10101                 records : records,
10102                 totalRecords : records.length
10103             };
10104     }
10105 });/*
10106  * - LGPL
10107  * * 
10108  */
10109
10110 /**
10111  * @class Roo.bootstrap.ComboBox
10112  * @extends Roo.bootstrap.TriggerField
10113  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
10114  * @cfg {Boolean} append (true|false) default false
10115  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
10116  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
10117  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
10118  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
10119  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
10120  * @constructor
10121  * Create a new ComboBox.
10122  * @param {Object} config Configuration options
10123  */
10124 Roo.bootstrap.ComboBox = function(config){
10125     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
10126     this.addEvents({
10127         /**
10128          * @event expand
10129          * Fires when the dropdown list is expanded
10130              * @param {Roo.bootstrap.ComboBox} combo This combo box
10131              */
10132         'expand' : true,
10133         /**
10134          * @event collapse
10135          * Fires when the dropdown list is collapsed
10136              * @param {Roo.bootstrap.ComboBox} combo This combo box
10137              */
10138         'collapse' : true,
10139         /**
10140          * @event beforeselect
10141          * Fires before a list item is selected. Return false to cancel the selection.
10142              * @param {Roo.bootstrap.ComboBox} combo This combo box
10143              * @param {Roo.data.Record} record The data record returned from the underlying store
10144              * @param {Number} index The index of the selected item in the dropdown list
10145              */
10146         'beforeselect' : true,
10147         /**
10148          * @event select
10149          * Fires when a list item is selected
10150              * @param {Roo.bootstrap.ComboBox} combo This combo box
10151              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
10152              * @param {Number} index The index of the selected item in the dropdown list
10153              */
10154         'select' : true,
10155         /**
10156          * @event beforequery
10157          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
10158          * The event object passed has these properties:
10159              * @param {Roo.bootstrap.ComboBox} combo This combo box
10160              * @param {String} query The query
10161              * @param {Boolean} forceAll true to force "all" query
10162              * @param {Boolean} cancel true to cancel the query
10163              * @param {Object} e The query event object
10164              */
10165         'beforequery': true,
10166          /**
10167          * @event add
10168          * Fires when the 'add' icon is pressed (add a listener to enable add button)
10169              * @param {Roo.bootstrap.ComboBox} combo This combo box
10170              */
10171         'add' : true,
10172         /**
10173          * @event edit
10174          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
10175              * @param {Roo.bootstrap.ComboBox} combo This combo box
10176              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
10177              */
10178         'edit' : true,
10179         /**
10180          * @event remove
10181          * Fires when the remove value from the combobox array
10182              * @param {Roo.bootstrap.ComboBox} combo This combo box
10183              */
10184         'remove' : true
10185         
10186     });
10187     
10188     this.item = [];
10189     this.tickItems = [];
10190     
10191     this.selectedIndex = -1;
10192     if(this.mode == 'local'){
10193         if(config.queryDelay === undefined){
10194             this.queryDelay = 10;
10195         }
10196         if(config.minChars === undefined){
10197             this.minChars = 0;
10198         }
10199     }
10200 };
10201
10202 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
10203      
10204     /**
10205      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
10206      * rendering into an Roo.Editor, defaults to false)
10207      */
10208     /**
10209      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
10210      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
10211      */
10212     /**
10213      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
10214      */
10215     /**
10216      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
10217      * the dropdown list (defaults to undefined, with no header element)
10218      */
10219
10220      /**
10221      * @cfg {String/Roo.Template} tpl The template to use to render the output
10222      */
10223      
10224      /**
10225      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
10226      */
10227     listWidth: undefined,
10228     /**
10229      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
10230      * mode = 'remote' or 'text' if mode = 'local')
10231      */
10232     displayField: undefined,
10233     /**
10234      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
10235      * mode = 'remote' or 'value' if mode = 'local'). 
10236      * Note: use of a valueField requires the user make a selection
10237      * in order for a value to be mapped.
10238      */
10239     valueField: undefined,
10240     
10241     
10242     /**
10243      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
10244      * field's data value (defaults to the underlying DOM element's name)
10245      */
10246     hiddenName: undefined,
10247     /**
10248      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
10249      */
10250     listClass: '',
10251     /**
10252      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
10253      */
10254     selectedClass: 'active',
10255     
10256     /**
10257      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
10258      */
10259     shadow:'sides',
10260     /**
10261      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
10262      * anchor positions (defaults to 'tl-bl')
10263      */
10264     listAlign: 'tl-bl?',
10265     /**
10266      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
10267      */
10268     maxHeight: 300,
10269     /**
10270      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
10271      * query specified by the allQuery config option (defaults to 'query')
10272      */
10273     triggerAction: 'query',
10274     /**
10275      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
10276      * (defaults to 4, does not apply if editable = false)
10277      */
10278     minChars : 4,
10279     /**
10280      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
10281      * delay (typeAheadDelay) if it matches a known value (defaults to false)
10282      */
10283     typeAhead: false,
10284     /**
10285      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
10286      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
10287      */
10288     queryDelay: 500,
10289     /**
10290      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
10291      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
10292      */
10293     pageSize: 0,
10294     /**
10295      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
10296      * when editable = true (defaults to false)
10297      */
10298     selectOnFocus:false,
10299     /**
10300      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
10301      */
10302     queryParam: 'query',
10303     /**
10304      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
10305      * when mode = 'remote' (defaults to 'Loading...')
10306      */
10307     loadingText: 'Loading...',
10308     /**
10309      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
10310      */
10311     resizable: false,
10312     /**
10313      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
10314      */
10315     handleHeight : 8,
10316     /**
10317      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
10318      * traditional select (defaults to true)
10319      */
10320     editable: true,
10321     /**
10322      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
10323      */
10324     allQuery: '',
10325     /**
10326      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
10327      */
10328     mode: 'remote',
10329     /**
10330      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
10331      * listWidth has a higher value)
10332      */
10333     minListWidth : 70,
10334     /**
10335      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
10336      * allow the user to set arbitrary text into the field (defaults to false)
10337      */
10338     forceSelection:false,
10339     /**
10340      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
10341      * if typeAhead = true (defaults to 250)
10342      */
10343     typeAheadDelay : 250,
10344     /**
10345      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
10346      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
10347      */
10348     valueNotFoundText : undefined,
10349     /**
10350      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
10351      */
10352     blockFocus : false,
10353     
10354     /**
10355      * @cfg {Boolean} disableClear Disable showing of clear button.
10356      */
10357     disableClear : false,
10358     /**
10359      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
10360      */
10361     alwaysQuery : false,
10362     
10363     /**
10364      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
10365      */
10366     multiple : false,
10367     
10368     //private
10369     addicon : false,
10370     editicon: false,
10371     
10372     page: 0,
10373     hasQuery: false,
10374     append: false,
10375     loadNext: false,
10376     autoFocus : true,
10377     tickable : false,
10378     btnPosition : 'right',
10379     triggerList : true,
10380     showToggleBtn : true,
10381     // element that contains real text value.. (when hidden is used..)
10382     
10383     getAutoCreate : function()
10384     {
10385         var cfg = false;
10386         
10387         /*
10388          *  Normal ComboBox
10389          */
10390         if(!this.tickable){
10391             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
10392             return cfg;
10393         }
10394         
10395         /*
10396          *  ComboBox with tickable selections
10397          */
10398              
10399         var align = this.labelAlign || this.parentLabelAlign();
10400         
10401         cfg = {
10402             cls : 'form-group roo-combobox-tickable' //input-group
10403         };
10404         
10405         
10406         var buttons = {
10407             tag : 'div',
10408             cls : 'tickable-buttons',
10409             cn : [
10410                 {
10411                     tag : 'button',
10412                     type : 'button',
10413                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
10414                     html : 'Edit'
10415                 },
10416                 {
10417                     tag : 'button',
10418                     type : 'button',
10419                     name : 'ok',
10420                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
10421                     html : 'Done'
10422                 },
10423                 {
10424                     tag : 'button',
10425                     type : 'button',
10426                     name : 'cancel',
10427                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
10428                     html : 'Cancel'
10429                 }
10430             ]
10431         };
10432         
10433         var _this = this;
10434         Roo.each(buttons.cn, function(c){
10435             if (_this.size) {
10436                 c.cls += ' btn-' + _this.size;
10437             }
10438
10439             if (_this.disabled) {
10440                 c.disabled = true;
10441             }
10442         });
10443         
10444         var box = {
10445             tag: 'div',
10446             cn: [
10447                 {
10448                     tag: 'input',
10449                     type : 'hidden',
10450                     cls: 'form-hidden-field'
10451                 },
10452                 {
10453                     tag: 'ul',
10454                     cls: 'select2-choices',
10455                     cn:[
10456                         {
10457                             tag: 'li',
10458                             cls: 'select2-search-field',
10459                             cn: [
10460
10461                                 buttons
10462                             ]
10463                         }
10464                     ]
10465                 }
10466             ]
10467         }
10468         
10469         var combobox = {
10470             cls: 'select2-container input-group select2-container-multi',
10471             cn: [
10472                 box
10473 //                {
10474 //                    tag: 'ul',
10475 //                    cls: 'typeahead typeahead-long dropdown-menu',
10476 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
10477 //                }
10478             ]
10479         };
10480         
10481         if (align ==='left' && this.fieldLabel.length) {
10482             
10483                 Roo.log("left and has label");
10484                 cfg.cn = [
10485                     
10486                     {
10487                         tag: 'label',
10488                         'for' :  id,
10489                         cls : 'control-label col-sm-' + this.labelWidth,
10490                         html : this.fieldLabel
10491                         
10492                     },
10493                     {
10494                         cls : "col-sm-" + (12 - this.labelWidth), 
10495                         cn: [
10496                             combobox
10497                         ]
10498                     }
10499                     
10500                 ];
10501         } else if ( this.fieldLabel.length) {
10502                 Roo.log(" label");
10503                  cfg.cn = [
10504                    
10505                     {
10506                         tag: 'label',
10507                         //cls : 'input-group-addon',
10508                         html : this.fieldLabel
10509                         
10510                     },
10511                     
10512                     combobox
10513                     
10514                 ];
10515
10516         } else {
10517             
10518                 Roo.log(" no label && no align");
10519                 cfg = combobox
10520                      
10521                 
10522         }
10523          
10524         var settings=this;
10525         ['xs','sm','md','lg'].map(function(size){
10526             if (settings[size]) {
10527                 cfg.cls += ' col-' + size + '-' + settings[size];
10528             }
10529         });
10530         
10531         return cfg;
10532         
10533     },
10534     
10535     // private
10536     initEvents: function()
10537     {
10538         
10539         if (!this.store) {
10540             throw "can not find store for combo";
10541         }
10542         this.store = Roo.factory(this.store, Roo.data);
10543         
10544         if(this.tickable){
10545             this.initTickableEvents();
10546             return;
10547         }
10548         
10549         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
10550         
10551         if(this.hiddenName){
10552             
10553             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10554             
10555             this.hiddenField.dom.value =
10556                 this.hiddenValue !== undefined ? this.hiddenValue :
10557                 this.value !== undefined ? this.value : '';
10558
10559             // prevent input submission
10560             this.el.dom.removeAttribute('name');
10561             this.hiddenField.dom.setAttribute('name', this.hiddenName);
10562              
10563              
10564         }
10565         //if(Roo.isGecko){
10566         //    this.el.dom.setAttribute('autocomplete', 'off');
10567         //}
10568         
10569         var cls = 'x-combo-list';
10570         
10571         //this.list = new Roo.Layer({
10572         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
10573         //});
10574         
10575         var _this = this;
10576         
10577         (function(){
10578             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
10579             _this.list.setWidth(lw);
10580         }).defer(100);
10581         
10582         this.list.on('mouseover', this.onViewOver, this);
10583         this.list.on('mousemove', this.onViewMove, this);
10584         
10585         this.list.on('scroll', this.onViewScroll, this);
10586         
10587         /*
10588         this.list.swallowEvent('mousewheel');
10589         this.assetHeight = 0;
10590
10591         if(this.title){
10592             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
10593             this.assetHeight += this.header.getHeight();
10594         }
10595
10596         this.innerList = this.list.createChild({cls:cls+'-inner'});
10597         this.innerList.on('mouseover', this.onViewOver, this);
10598         this.innerList.on('mousemove', this.onViewMove, this);
10599         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
10600         
10601         if(this.allowBlank && !this.pageSize && !this.disableClear){
10602             this.footer = this.list.createChild({cls:cls+'-ft'});
10603             this.pageTb = new Roo.Toolbar(this.footer);
10604            
10605         }
10606         if(this.pageSize){
10607             this.footer = this.list.createChild({cls:cls+'-ft'});
10608             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
10609                     {pageSize: this.pageSize});
10610             
10611         }
10612         
10613         if (this.pageTb && this.allowBlank && !this.disableClear) {
10614             var _this = this;
10615             this.pageTb.add(new Roo.Toolbar.Fill(), {
10616                 cls: 'x-btn-icon x-btn-clear',
10617                 text: '&#160;',
10618                 handler: function()
10619                 {
10620                     _this.collapse();
10621                     _this.clearValue();
10622                     _this.onSelect(false, -1);
10623                 }
10624             });
10625         }
10626         if (this.footer) {
10627             this.assetHeight += this.footer.getHeight();
10628         }
10629         */
10630             
10631         if(!this.tpl){
10632             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
10633         }
10634
10635         this.view = new Roo.View(this.list, this.tpl, {
10636             singleSelect:true, store: this.store, selectedClass: this.selectedClass
10637         });
10638         //this.view.wrapEl.setDisplayed(false);
10639         this.view.on('click', this.onViewClick, this);
10640         
10641         
10642         
10643         this.store.on('beforeload', this.onBeforeLoad, this);
10644         this.store.on('load', this.onLoad, this);
10645         this.store.on('loadexception', this.onLoadException, this);
10646         /*
10647         if(this.resizable){
10648             this.resizer = new Roo.Resizable(this.list,  {
10649                pinned:true, handles:'se'
10650             });
10651             this.resizer.on('resize', function(r, w, h){
10652                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
10653                 this.listWidth = w;
10654                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
10655                 this.restrictHeight();
10656             }, this);
10657             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
10658         }
10659         */
10660         if(!this.editable){
10661             this.editable = true;
10662             this.setEditable(false);
10663         }
10664         
10665         /*
10666         
10667         if (typeof(this.events.add.listeners) != 'undefined') {
10668             
10669             this.addicon = this.wrap.createChild(
10670                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
10671        
10672             this.addicon.on('click', function(e) {
10673                 this.fireEvent('add', this);
10674             }, this);
10675         }
10676         if (typeof(this.events.edit.listeners) != 'undefined') {
10677             
10678             this.editicon = this.wrap.createChild(
10679                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
10680             if (this.addicon) {
10681                 this.editicon.setStyle('margin-left', '40px');
10682             }
10683             this.editicon.on('click', function(e) {
10684                 
10685                 // we fire even  if inothing is selected..
10686                 this.fireEvent('edit', this, this.lastData );
10687                 
10688             }, this);
10689         }
10690         */
10691         
10692         this.keyNav = new Roo.KeyNav(this.inputEl(), {
10693             "up" : function(e){
10694                 this.inKeyMode = true;
10695                 this.selectPrev();
10696             },
10697
10698             "down" : function(e){
10699                 if(!this.isExpanded()){
10700                     this.onTriggerClick();
10701                 }else{
10702                     this.inKeyMode = true;
10703                     this.selectNext();
10704                 }
10705             },
10706
10707             "enter" : function(e){
10708 //                this.onViewClick();
10709                 //return true;
10710                 this.collapse();
10711                 
10712                 if(this.fireEvent("specialkey", this, e)){
10713                     this.onViewClick(false);
10714                 }
10715                 
10716                 return true;
10717             },
10718
10719             "esc" : function(e){
10720                 this.collapse();
10721             },
10722
10723             "tab" : function(e){
10724                 this.collapse();
10725                 
10726                 if(this.fireEvent("specialkey", this, e)){
10727                     this.onViewClick(false);
10728                 }
10729                 
10730                 return true;
10731             },
10732
10733             scope : this,
10734
10735             doRelay : function(foo, bar, hname){
10736                 if(hname == 'down' || this.scope.isExpanded()){
10737                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
10738                 }
10739                 return true;
10740             },
10741
10742             forceKeyDown: true
10743         });
10744         
10745         
10746         this.queryDelay = Math.max(this.queryDelay || 10,
10747                 this.mode == 'local' ? 10 : 250);
10748         
10749         
10750         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
10751         
10752         if(this.typeAhead){
10753             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
10754         }
10755         if(this.editable !== false){
10756             this.inputEl().on("keyup", this.onKeyUp, this);
10757         }
10758         if(this.forceSelection){
10759             this.inputEl().on('blur', this.doForce, this);
10760         }
10761         
10762         if(this.multiple){
10763             this.choices = this.el.select('ul.select2-choices', true).first();
10764             this.searchField = this.el.select('ul li.select2-search-field', true).first();
10765         }
10766     },
10767     
10768     initTickableEvents: function()
10769     {   
10770         this.createList();
10771         
10772         if(this.hiddenName){
10773             
10774             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10775             
10776             this.hiddenField.dom.value =
10777                 this.hiddenValue !== undefined ? this.hiddenValue :
10778                 this.value !== undefined ? this.value : '';
10779
10780             // prevent input submission
10781             this.el.dom.removeAttribute('name');
10782             this.hiddenField.dom.setAttribute('name', this.hiddenName);
10783              
10784              
10785         }
10786         
10787 //        this.list = this.el.select('ul.dropdown-menu',true).first();
10788         
10789         this.choices = this.el.select('ul.select2-choices', true).first();
10790         this.searchField = this.el.select('ul li.select2-search-field', true).first();
10791         if(this.triggerList){
10792             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
10793         }
10794          
10795         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
10796         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
10797         
10798         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
10799         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
10800         
10801         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
10802         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
10803         
10804         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
10805         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
10806         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
10807         
10808         this.okBtn.hide();
10809         this.cancelBtn.hide();
10810         
10811         var _this = this;
10812         
10813         (function(){
10814             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
10815             _this.list.setWidth(lw);
10816         }).defer(100);
10817         
10818         this.list.on('mouseover', this.onViewOver, this);
10819         this.list.on('mousemove', this.onViewMove, this);
10820         
10821         this.list.on('scroll', this.onViewScroll, this);
10822         
10823         if(!this.tpl){
10824             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>';
10825         }
10826
10827         this.view = new Roo.View(this.list, this.tpl, {
10828             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
10829         });
10830         
10831         //this.view.wrapEl.setDisplayed(false);
10832         this.view.on('click', this.onViewClick, this);
10833         
10834         
10835         
10836         this.store.on('beforeload', this.onBeforeLoad, this);
10837         this.store.on('load', this.onLoad, this);
10838         this.store.on('loadexception', this.onLoadException, this);
10839         
10840 //        this.keyNav = new Roo.KeyNav(this.inputEl(), {
10841 //            "up" : function(e){
10842 //                this.inKeyMode = true;
10843 //                this.selectPrev();
10844 //            },
10845 //
10846 //            "down" : function(e){
10847 //                if(!this.isExpanded()){
10848 //                    this.onTriggerClick();
10849 //                }else{
10850 //                    this.inKeyMode = true;
10851 //                    this.selectNext();
10852 //                }
10853 //            },
10854 //
10855 //            "enter" : function(e){
10856 ////                this.onViewClick();
10857 //                //return true;
10858 //                this.collapse();
10859 //                
10860 //                if(this.fireEvent("specialkey", this, e)){
10861 //                    this.onViewClick(false);
10862 //                }
10863 //                
10864 //                return true;
10865 //            },
10866 //
10867 //            "esc" : function(e){
10868 //                this.collapse();
10869 //            },
10870 //
10871 //            "tab" : function(e){
10872 //                this.collapse();
10873 //                
10874 //                if(this.fireEvent("specialkey", this, e)){
10875 //                    this.onViewClick(false);
10876 //                }
10877 //                
10878 //                return true;
10879 //            },
10880 //
10881 //            scope : this,
10882 //
10883 //            doRelay : function(foo, bar, hname){
10884 //                if(hname == 'down' || this.scope.isExpanded()){
10885 //                   return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
10886 //                }
10887 //                return true;
10888 //            },
10889 //
10890 //            forceKeyDown: true
10891 //        });
10892         
10893         
10894         this.queryDelay = Math.max(this.queryDelay || 10,
10895                 this.mode == 'local' ? 10 : 250);
10896         
10897         
10898         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
10899         
10900         if(this.typeAhead){
10901             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
10902         }
10903     },
10904
10905     onDestroy : function(){
10906         if(this.view){
10907             this.view.setStore(null);
10908             this.view.el.removeAllListeners();
10909             this.view.el.remove();
10910             this.view.purgeListeners();
10911         }
10912         if(this.list){
10913             this.list.dom.innerHTML  = '';
10914         }
10915         
10916         if(this.store){
10917             this.store.un('beforeload', this.onBeforeLoad, this);
10918             this.store.un('load', this.onLoad, this);
10919             this.store.un('loadexception', this.onLoadException, this);
10920         }
10921         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
10922     },
10923
10924     // private
10925     fireKey : function(e){
10926         if(e.isNavKeyPress() && !this.list.isVisible()){
10927             this.fireEvent("specialkey", this, e);
10928         }
10929     },
10930
10931     // private
10932     onResize: function(w, h){
10933 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
10934 //        
10935 //        if(typeof w != 'number'){
10936 //            // we do not handle it!?!?
10937 //            return;
10938 //        }
10939 //        var tw = this.trigger.getWidth();
10940 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
10941 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
10942 //        var x = w - tw;
10943 //        this.inputEl().setWidth( this.adjustWidth('input', x));
10944 //            
10945 //        //this.trigger.setStyle('left', x+'px');
10946 //        
10947 //        if(this.list && this.listWidth === undefined){
10948 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
10949 //            this.list.setWidth(lw);
10950 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
10951 //        }
10952         
10953     
10954         
10955     },
10956
10957     /**
10958      * Allow or prevent the user from directly editing the field text.  If false is passed,
10959      * the user will only be able to select from the items defined in the dropdown list.  This method
10960      * is the runtime equivalent of setting the 'editable' config option at config time.
10961      * @param {Boolean} value True to allow the user to directly edit the field text
10962      */
10963     setEditable : function(value){
10964         if(value == this.editable){
10965             return;
10966         }
10967         this.editable = value;
10968         if(!value){
10969             this.inputEl().dom.setAttribute('readOnly', true);
10970             this.inputEl().on('mousedown', this.onTriggerClick,  this);
10971             this.inputEl().addClass('x-combo-noedit');
10972         }else{
10973             this.inputEl().dom.setAttribute('readOnly', false);
10974             this.inputEl().un('mousedown', this.onTriggerClick,  this);
10975             this.inputEl().removeClass('x-combo-noedit');
10976         }
10977     },
10978
10979     // private
10980     
10981     onBeforeLoad : function(combo,opts){
10982         if(!this.hasFocus){
10983             return;
10984         }
10985          if (!opts.add) {
10986             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
10987          }
10988 //        this.restrictHeight();
10989         this.selectedIndex = -1;
10990     },
10991
10992     // private
10993     onLoad : function(){
10994         
10995         this.hasQuery = false;
10996         
10997         if(!this.hasFocus){
10998             return;
10999         }
11000         
11001         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11002             this.loading.hide();
11003         }
11004         
11005         if(this.store.getCount() > 0){
11006             this.expand();
11007 //            this.restrictHeight();
11008             if(this.lastQuery == this.allQuery){
11009                 if(this.editable && !this.tickable){
11010                     this.inputEl().dom.select();
11011                 }
11012                 if(!this.selectByValue(this.value, true) && this.autoFocus){
11013                     this.select(0, true);
11014                 }
11015             }else{
11016                 if(this.autoFocus){
11017                     this.selectNext();
11018                 }
11019                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
11020                     this.taTask.delay(this.typeAheadDelay);
11021                 }
11022             }
11023         }else{
11024             this.onEmptyResults();
11025         }
11026         
11027         //this.el.focus();
11028     },
11029     // private
11030     onLoadException : function()
11031     {
11032         this.hasQuery = false;
11033         
11034         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11035             this.loading.hide();
11036         }
11037         
11038         this.collapse();
11039         Roo.log(this.store.reader.jsonData);
11040         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
11041             // fixme
11042             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
11043         }
11044         
11045         
11046     },
11047     // private
11048     onTypeAhead : function(){
11049         if(this.store.getCount() > 0){
11050             var r = this.store.getAt(0);
11051             var newValue = r.data[this.displayField];
11052             var len = newValue.length;
11053             var selStart = this.getRawValue().length;
11054             
11055             if(selStart != len){
11056                 this.setRawValue(newValue);
11057                 this.selectText(selStart, newValue.length);
11058             }
11059         }
11060     },
11061
11062     // private
11063     onSelect : function(record, index){
11064         
11065         if(this.fireEvent('beforeselect', this, record, index) !== false){
11066         
11067             this.setFromData(index > -1 ? record.data : false);
11068             
11069             this.collapse();
11070             this.fireEvent('select', this, record, index);
11071         }
11072     },
11073
11074     /**
11075      * Returns the currently selected field value or empty string if no value is set.
11076      * @return {String} value The selected value
11077      */
11078     getValue : function(){
11079         
11080         if(this.multiple){
11081             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
11082         }
11083         
11084         if(this.valueField){
11085             return typeof this.value != 'undefined' ? this.value : '';
11086         }else{
11087             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
11088         }
11089     },
11090
11091     /**
11092      * Clears any text/value currently set in the field
11093      */
11094     clearValue : function(){
11095         if(this.hiddenField){
11096             this.hiddenField.dom.value = '';
11097         }
11098         this.value = '';
11099         this.setRawValue('');
11100         this.lastSelectionText = '';
11101         
11102     },
11103
11104     /**
11105      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
11106      * will be displayed in the field.  If the value does not match the data value of an existing item,
11107      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
11108      * Otherwise the field will be blank (although the value will still be set).
11109      * @param {String} value The value to match
11110      */
11111     setValue : function(v){
11112         if(this.multiple){
11113             this.syncValue();
11114             return;
11115         }
11116         
11117         var text = v;
11118         if(this.valueField){
11119             var r = this.findRecord(this.valueField, v);
11120             if(r){
11121                 text = r.data[this.displayField];
11122             }else if(this.valueNotFoundText !== undefined){
11123                 text = this.valueNotFoundText;
11124             }
11125         }
11126         this.lastSelectionText = text;
11127         if(this.hiddenField){
11128             this.hiddenField.dom.value = v;
11129         }
11130         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
11131         this.value = v;
11132     },
11133     /**
11134      * @property {Object} the last set data for the element
11135      */
11136     
11137     lastData : false,
11138     /**
11139      * Sets the value of the field based on a object which is related to the record format for the store.
11140      * @param {Object} value the value to set as. or false on reset?
11141      */
11142     setFromData : function(o){
11143         
11144         if(this.multiple){
11145             if(typeof o.display_name !== 'string'){
11146                 for(var i=0;i<o.display_name.length;i++){
11147                     this.addItem({'id':o.id[i],'display_name':o.display_name[i]});
11148                 }
11149                 return;
11150             }
11151             this.addItem(o);
11152             return;
11153         }
11154             
11155         var dv = ''; // display value
11156         var vv = ''; // value value..
11157         this.lastData = o;
11158         if (this.displayField) {
11159             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11160         } else {
11161             // this is an error condition!!!
11162             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
11163         }
11164         
11165         if(this.valueField){
11166             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
11167         }
11168         
11169         if(this.hiddenField){
11170             this.hiddenField.dom.value = vv;
11171             
11172             this.lastSelectionText = dv;
11173             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11174             this.value = vv;
11175             return;
11176         }
11177         // no hidden field.. - we store the value in 'value', but still display
11178         // display field!!!!
11179         this.lastSelectionText = dv;
11180         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11181         this.value = vv;
11182         
11183         
11184     },
11185     // private
11186     reset : function(){
11187         // overridden so that last data is reset..
11188         this.setValue(this.originalValue);
11189         this.clearInvalid();
11190         this.lastData = false;
11191         if (this.view) {
11192             this.view.clearSelections();
11193         }
11194     },
11195     // private
11196     findRecord : function(prop, value){
11197         var record;
11198         if(this.store.getCount() > 0){
11199             this.store.each(function(r){
11200                 if(r.data[prop] == value){
11201                     record = r;
11202                     return false;
11203                 }
11204                 return true;
11205             });
11206         }
11207         return record;
11208     },
11209     
11210     getName: function()
11211     {
11212         // returns hidden if it's set..
11213         if (!this.rendered) {return ''};
11214         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
11215         
11216     },
11217     // private
11218     onViewMove : function(e, t){
11219         this.inKeyMode = false;
11220     },
11221
11222     // private
11223     onViewOver : function(e, t){
11224         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
11225             return;
11226         }
11227         var item = this.view.findItemFromChild(t);
11228         
11229         if(item){
11230             var index = this.view.indexOf(item);
11231             this.select(index, false);
11232         }
11233     },
11234
11235     // private
11236     onViewClick : function(view, doFocus, el, e)
11237     {
11238         var index = this.view.getSelectedIndexes()[0];
11239         
11240         var r = this.store.getAt(index);
11241         
11242         if(this.tickable){
11243             
11244             if(e.getTarget().nodeName.toLowerCase() != 'input'){
11245                 return;
11246             }
11247             
11248             var rm = false;
11249             var _this = this;
11250             
11251             Roo.each(this.tickItems, function(v,k){
11252                 
11253                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
11254                     _this.tickItems.splice(k, 1);
11255                     rm = true;
11256                     return;
11257                 }
11258             })
11259             
11260             if(rm){
11261                 return;
11262             }
11263             
11264             this.tickItems.push(r.data);
11265             return;
11266         }
11267         
11268         if(r){
11269             this.onSelect(r, index);
11270         }
11271         if(doFocus !== false && !this.blockFocus){
11272             this.inputEl().focus();
11273         }
11274     },
11275
11276     // private
11277     restrictHeight : function(){
11278         //this.innerList.dom.style.height = '';
11279         //var inner = this.innerList.dom;
11280         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
11281         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
11282         //this.list.beginUpdate();
11283         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
11284         this.list.alignTo(this.inputEl(), this.listAlign);
11285         this.list.alignTo(this.inputEl(), this.listAlign);
11286         //this.list.endUpdate();
11287     },
11288
11289     // private
11290     onEmptyResults : function(){
11291         this.collapse();
11292     },
11293
11294     /**
11295      * Returns true if the dropdown list is expanded, else false.
11296      */
11297     isExpanded : function(){
11298         return this.list.isVisible();
11299     },
11300
11301     /**
11302      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
11303      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11304      * @param {String} value The data value of the item to select
11305      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11306      * selected item if it is not currently in view (defaults to true)
11307      * @return {Boolean} True if the value matched an item in the list, else false
11308      */
11309     selectByValue : function(v, scrollIntoView){
11310         if(v !== undefined && v !== null){
11311             var r = this.findRecord(this.valueField || this.displayField, v);
11312             if(r){
11313                 this.select(this.store.indexOf(r), scrollIntoView);
11314                 return true;
11315             }
11316         }
11317         return false;
11318     },
11319
11320     /**
11321      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
11322      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11323      * @param {Number} index The zero-based index of the list item to select
11324      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11325      * selected item if it is not currently in view (defaults to true)
11326      */
11327     select : function(index, scrollIntoView){
11328         this.selectedIndex = index;
11329         this.view.select(index);
11330         if(scrollIntoView !== false){
11331             var el = this.view.getNode(index);
11332             if(el && !this.multiple && !this.tickable){
11333                 this.list.scrollChildIntoView(el, false);
11334             }
11335         }
11336     },
11337
11338     // private
11339     selectNext : function(){
11340         var ct = this.store.getCount();
11341         if(ct > 0){
11342             if(this.selectedIndex == -1){
11343                 this.select(0);
11344             }else if(this.selectedIndex < ct-1){
11345                 this.select(this.selectedIndex+1);
11346             }
11347         }
11348     },
11349
11350     // private
11351     selectPrev : function(){
11352         var ct = this.store.getCount();
11353         if(ct > 0){
11354             if(this.selectedIndex == -1){
11355                 this.select(0);
11356             }else if(this.selectedIndex != 0){
11357                 this.select(this.selectedIndex-1);
11358             }
11359         }
11360     },
11361
11362     // private
11363     onKeyUp : function(e){
11364         if(this.editable !== false && !e.isSpecialKey()){
11365             this.lastKey = e.getKey();
11366             this.dqTask.delay(this.queryDelay);
11367         }
11368     },
11369
11370     // private
11371     validateBlur : function(){
11372         return !this.list || !this.list.isVisible();   
11373     },
11374
11375     // private
11376     initQuery : function(){
11377         this.doQuery(this.getRawValue());
11378     },
11379
11380     // private
11381     doForce : function(){
11382         if(this.inputEl().dom.value.length > 0){
11383             this.inputEl().dom.value =
11384                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
11385              
11386         }
11387     },
11388
11389     /**
11390      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
11391      * query allowing the query action to be canceled if needed.
11392      * @param {String} query The SQL query to execute
11393      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
11394      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
11395      * saved in the current store (defaults to false)
11396      */
11397     doQuery : function(q, forceAll){
11398         
11399         if(q === undefined || q === null){
11400             q = '';
11401         }
11402         var qe = {
11403             query: q,
11404             forceAll: forceAll,
11405             combo: this,
11406             cancel:false
11407         };
11408         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
11409             return false;
11410         }
11411         q = qe.query;
11412         
11413         forceAll = qe.forceAll;
11414         if(forceAll === true || (q.length >= this.minChars)){
11415             
11416             this.hasQuery = true;
11417             
11418             if(this.lastQuery != q || this.alwaysQuery){
11419                 this.lastQuery = q;
11420                 if(this.mode == 'local'){
11421                     this.selectedIndex = -1;
11422                     if(forceAll){
11423                         this.store.clearFilter();
11424                     }else{
11425                         this.store.filter(this.displayField, q);
11426                     }
11427                     this.onLoad();
11428                 }else{
11429                     this.store.baseParams[this.queryParam] = q;
11430                     
11431                     var options = {params : this.getParams(q)};
11432                     
11433                     if(this.loadNext){
11434                         options.add = true;
11435                         options.params.start = this.page * this.pageSize;
11436                     }
11437                     
11438                     this.store.load(options);
11439                     /*
11440                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
11441                      *  we should expand the list on onLoad
11442                      *  so command out it
11443                      */
11444 //                    this.expand();
11445                 }
11446             }else{
11447                 this.selectedIndex = -1;
11448                 this.onLoad();   
11449             }
11450         }
11451         
11452         this.loadNext = false;
11453     },
11454
11455     // private
11456     getParams : function(q){
11457         var p = {};
11458         //p[this.queryParam] = q;
11459         
11460         if(this.pageSize){
11461             p.start = 0;
11462             p.limit = this.pageSize;
11463         }
11464         return p;
11465     },
11466
11467     /**
11468      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
11469      */
11470     collapse : function(){
11471         if(!this.isExpanded()){
11472             return;
11473         }
11474         
11475 //        this.hasFocus = false;
11476         
11477         this.list.hide();
11478         
11479         if(this.tickable){
11480             this.okBtn.hide();
11481             this.cancelBtn.hide();
11482             this.trigger.show();
11483         }
11484         
11485         Roo.get(document).un('mousedown', this.collapseIf, this);
11486         Roo.get(document).un('mousewheel', this.collapseIf, this);
11487         if (!this.editable) {
11488             Roo.get(document).un('keydown', this.listKeyPress, this);
11489         }
11490         this.fireEvent('collapse', this);
11491     },
11492
11493     // private
11494     collapseIf : function(e){
11495         var in_combo  = e.within(this.el);
11496         var in_list =  e.within(this.list);
11497         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
11498         
11499         if (in_combo || in_list || is_list) {
11500             //e.stopPropagation();
11501             return;
11502         }
11503         
11504         if(this.tickable){
11505             this.onTickableFooterButtonClick(e, false, false);
11506         }
11507
11508         this.collapse();
11509         
11510     },
11511
11512     /**
11513      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
11514      */
11515     expand : function(){
11516        
11517         if(this.isExpanded() || !this.hasFocus){
11518             return;
11519         }
11520          Roo.log('expand');
11521         
11522         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
11523         this.list.setWidth(lw);
11524             
11525         this.list.show();
11526         
11527         this.restrictHeight();
11528         
11529         if(this.tickable){
11530             
11531             this.tickItems = Roo.apply([], this.item);
11532             
11533             this.okBtn.show();
11534             this.cancelBtn.show();
11535             this.trigger.hide();
11536             
11537         }
11538         
11539         Roo.get(document).on('mousedown', this.collapseIf, this);
11540         Roo.get(document).on('mousewheel', this.collapseIf, this);
11541         if (!this.editable) {
11542             Roo.get(document).on('keydown', this.listKeyPress, this);
11543         }
11544         
11545         this.fireEvent('expand', this);
11546     },
11547
11548     // private
11549     // Implements the default empty TriggerField.onTriggerClick function
11550     onTriggerClick : function(e)
11551     {
11552         Roo.log('trigger click');
11553         
11554         if(this.disabled || !this.triggerList){
11555             return;
11556         }
11557         
11558         this.page = 0;
11559         this.loadNext = false;
11560         
11561         if(this.isExpanded()){
11562             this.collapse();
11563             if (!this.blockFocus) {
11564                 this.inputEl().focus();
11565             }
11566             
11567         }else {
11568             this.hasFocus = true;
11569             if(this.triggerAction == 'all') {
11570                 this.doQuery(this.allQuery, true);
11571             } else {
11572                 this.doQuery(this.getRawValue());
11573             }
11574             if (!this.blockFocus) {
11575                 this.inputEl().focus();
11576             }
11577         }
11578     },
11579     
11580     onTickableTriggerClick : function(e)
11581     {
11582         if(this.disabled){
11583             return;
11584         }
11585         
11586         this.page = 0;
11587         this.loadNext = false;
11588         this.hasFocus = true;
11589         
11590         if(this.triggerAction == 'all') {
11591             this.doQuery(this.allQuery, true);
11592         } else {
11593             this.doQuery(this.getRawValue());
11594         }
11595     },
11596     
11597     onSearchFieldClick : function(e)
11598     {
11599         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
11600             return;
11601         }
11602         
11603         this.page = 0;
11604         this.loadNext = false;
11605         this.hasFocus = true;
11606         
11607         if(this.triggerAction == 'all') {
11608             this.doQuery(this.allQuery, true);
11609         } else {
11610             this.doQuery(this.getRawValue());
11611         }
11612     },
11613     
11614     listKeyPress : function(e)
11615     {
11616         //Roo.log('listkeypress');
11617         // scroll to first matching element based on key pres..
11618         if (e.isSpecialKey()) {
11619             return false;
11620         }
11621         var k = String.fromCharCode(e.getKey()).toUpperCase();
11622         //Roo.log(k);
11623         var match  = false;
11624         var csel = this.view.getSelectedNodes();
11625         var cselitem = false;
11626         if (csel.length) {
11627             var ix = this.view.indexOf(csel[0]);
11628             cselitem  = this.store.getAt(ix);
11629             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
11630                 cselitem = false;
11631             }
11632             
11633         }
11634         
11635         this.store.each(function(v) { 
11636             if (cselitem) {
11637                 // start at existing selection.
11638                 if (cselitem.id == v.id) {
11639                     cselitem = false;
11640                 }
11641                 return true;
11642             }
11643                 
11644             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
11645                 match = this.store.indexOf(v);
11646                 return false;
11647             }
11648             return true;
11649         }, this);
11650         
11651         if (match === false) {
11652             return true; // no more action?
11653         }
11654         // scroll to?
11655         this.view.select(match);
11656         var sn = Roo.get(this.view.getSelectedNodes()[0])
11657         //sn.scrollIntoView(sn.dom.parentNode, false);
11658     },
11659     
11660     onViewScroll : function(e, t){
11661         
11662         if(this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
11663             return;
11664         }
11665         
11666         this.hasQuery = true;
11667         
11668         this.loading = this.list.select('.loading', true).first();
11669         
11670         if(this.loading === null){
11671             this.list.createChild({
11672                 tag: 'div',
11673                 cls: 'loading select2-more-results select2-active',
11674                 html: 'Loading more results...'
11675             })
11676             
11677             this.loading = this.list.select('.loading', true).first();
11678             
11679             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
11680             
11681             this.loading.hide();
11682         }
11683         
11684         this.loading.show();
11685         
11686         var _combo = this;
11687         
11688         this.page++;
11689         this.loadNext = true;
11690         
11691         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
11692         
11693         return;
11694     },
11695     
11696     addItem : function(o)
11697     {   
11698         var dv = ''; // display value
11699         
11700         if (this.displayField) {
11701             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11702         } else {
11703             // this is an error condition!!!
11704             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
11705         }
11706         
11707         if(!dv.length){
11708             return;
11709         }
11710         
11711         var choice = this.choices.createChild({
11712             tag: 'li',
11713             cls: 'select2-search-choice',
11714             cn: [
11715                 {
11716                     tag: 'div',
11717                     html: dv
11718                 },
11719                 {
11720                     tag: 'a',
11721                     href: '#',
11722                     cls: 'select2-search-choice-close',
11723                     tabindex: '-1'
11724                 }
11725             ]
11726             
11727         }, this.searchField);
11728         
11729         var close = choice.select('a.select2-search-choice-close', true).first()
11730         
11731         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
11732         
11733         this.item.push(o);
11734         
11735         this.lastData = o;
11736         
11737         this.syncValue();
11738         
11739         this.inputEl().dom.value = '';
11740         
11741     },
11742     
11743     onRemoveItem : function(e, _self, o)
11744     {
11745         e.preventDefault();
11746         var index = this.item.indexOf(o.data) * 1;
11747         
11748         if( index < 0){
11749             Roo.log('not this item?!');
11750             return;
11751         }
11752         
11753         this.item.splice(index, 1);
11754         o.item.remove();
11755         
11756         this.syncValue();
11757         
11758         this.fireEvent('remove', this, e);
11759         
11760     },
11761     
11762     syncValue : function()
11763     {
11764         if(!this.item.length){
11765             this.clearValue();
11766             return;
11767         }
11768             
11769         var value = [];
11770         var _this = this;
11771         Roo.each(this.item, function(i){
11772             if(_this.valueField){
11773                 value.push(i[_this.valueField]);
11774                 return;
11775             }
11776
11777             value.push(i);
11778         });
11779
11780         this.value = value.join(',');
11781
11782         if(this.hiddenField){
11783             this.hiddenField.dom.value = this.value;
11784         }
11785     },
11786     
11787     clearItem : function()
11788     {
11789         if(!this.multiple){
11790             return;
11791         }
11792         
11793         this.item = [];
11794         
11795         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
11796            c.remove();
11797         });
11798         
11799         this.syncValue();
11800     },
11801     
11802     inputEl: function ()
11803     {
11804         if(this.tickable){
11805             return this.searchField;
11806         }
11807         return this.el.select('input.form-control',true).first();
11808     },
11809     
11810     
11811     onTickableFooterButtonClick : function(e, btn, el)
11812     {
11813         e.preventDefault();
11814         
11815         if(btn && btn.name == 'cancel'){
11816             this.tickItems = Roo.apply([], this.item);
11817             this.collapse();
11818             return;
11819         }
11820         
11821         this.clearItem();
11822         
11823         var _this = this;
11824         
11825         Roo.each(this.tickItems, function(o){
11826             _this.addItem(o);
11827         });
11828         
11829         this.collapse();
11830         
11831     }
11832     
11833     
11834
11835     /** 
11836     * @cfg {Boolean} grow 
11837     * @hide 
11838     */
11839     /** 
11840     * @cfg {Number} growMin 
11841     * @hide 
11842     */
11843     /** 
11844     * @cfg {Number} growMax 
11845     * @hide 
11846     */
11847     /**
11848      * @hide
11849      * @method autoSize
11850      */
11851 });
11852 /*
11853  * Based on:
11854  * Ext JS Library 1.1.1
11855  * Copyright(c) 2006-2007, Ext JS, LLC.
11856  *
11857  * Originally Released Under LGPL - original licence link has changed is not relivant.
11858  *
11859  * Fork - LGPL
11860  * <script type="text/javascript">
11861  */
11862
11863 /**
11864  * @class Roo.View
11865  * @extends Roo.util.Observable
11866  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
11867  * This class also supports single and multi selection modes. <br>
11868  * Create a data model bound view:
11869  <pre><code>
11870  var store = new Roo.data.Store(...);
11871
11872  var view = new Roo.View({
11873     el : "my-element",
11874     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
11875  
11876     singleSelect: true,
11877     selectedClass: "ydataview-selected",
11878     store: store
11879  });
11880
11881  // listen for node click?
11882  view.on("click", function(vw, index, node, e){
11883  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
11884  });
11885
11886  // load XML data
11887  dataModel.load("foobar.xml");
11888  </code></pre>
11889  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
11890  * <br><br>
11891  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
11892  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
11893  * 
11894  * Note: old style constructor is still suported (container, template, config)
11895  * 
11896  * @constructor
11897  * Create a new View
11898  * @param {Object} config The config object
11899  * 
11900  */
11901 Roo.View = function(config, depreciated_tpl, depreciated_config){
11902     
11903     this.parent = false;
11904     
11905     if (typeof(depreciated_tpl) == 'undefined') {
11906         // new way.. - universal constructor.
11907         Roo.apply(this, config);
11908         this.el  = Roo.get(this.el);
11909     } else {
11910         // old format..
11911         this.el  = Roo.get(config);
11912         this.tpl = depreciated_tpl;
11913         Roo.apply(this, depreciated_config);
11914     }
11915     this.wrapEl  = this.el.wrap().wrap();
11916     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
11917     
11918     
11919     if(typeof(this.tpl) == "string"){
11920         this.tpl = new Roo.Template(this.tpl);
11921     } else {
11922         // support xtype ctors..
11923         this.tpl = new Roo.factory(this.tpl, Roo);
11924     }
11925     
11926     
11927     this.tpl.compile();
11928     
11929     /** @private */
11930     this.addEvents({
11931         /**
11932          * @event beforeclick
11933          * Fires before a click is processed. Returns false to cancel the default action.
11934          * @param {Roo.View} this
11935          * @param {Number} index The index of the target node
11936          * @param {HTMLElement} node The target node
11937          * @param {Roo.EventObject} e The raw event object
11938          */
11939             "beforeclick" : true,
11940         /**
11941          * @event click
11942          * Fires when a template node is clicked.
11943          * @param {Roo.View} this
11944          * @param {Number} index The index of the target node
11945          * @param {HTMLElement} node The target node
11946          * @param {Roo.EventObject} e The raw event object
11947          */
11948             "click" : true,
11949         /**
11950          * @event dblclick
11951          * Fires when a template node is double clicked.
11952          * @param {Roo.View} this
11953          * @param {Number} index The index of the target node
11954          * @param {HTMLElement} node The target node
11955          * @param {Roo.EventObject} e The raw event object
11956          */
11957             "dblclick" : true,
11958         /**
11959          * @event contextmenu
11960          * Fires when a template node is right clicked.
11961          * @param {Roo.View} this
11962          * @param {Number} index The index of the target node
11963          * @param {HTMLElement} node The target node
11964          * @param {Roo.EventObject} e The raw event object
11965          */
11966             "contextmenu" : true,
11967         /**
11968          * @event selectionchange
11969          * Fires when the selected nodes change.
11970          * @param {Roo.View} this
11971          * @param {Array} selections Array of the selected nodes
11972          */
11973             "selectionchange" : true,
11974     
11975         /**
11976          * @event beforeselect
11977          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
11978          * @param {Roo.View} this
11979          * @param {HTMLElement} node The node to be selected
11980          * @param {Array} selections Array of currently selected nodes
11981          */
11982             "beforeselect" : true,
11983         /**
11984          * @event preparedata
11985          * Fires on every row to render, to allow you to change the data.
11986          * @param {Roo.View} this
11987          * @param {Object} data to be rendered (change this)
11988          */
11989           "preparedata" : true
11990           
11991           
11992         });
11993
11994
11995
11996     this.el.on({
11997         "click": this.onClick,
11998         "dblclick": this.onDblClick,
11999         "contextmenu": this.onContextMenu,
12000         scope:this
12001     });
12002
12003     this.selections = [];
12004     this.nodes = [];
12005     this.cmp = new Roo.CompositeElementLite([]);
12006     if(this.store){
12007         this.store = Roo.factory(this.store, Roo.data);
12008         this.setStore(this.store, true);
12009     }
12010     
12011     if ( this.footer && this.footer.xtype) {
12012            
12013          var fctr = this.wrapEl.appendChild(document.createElement("div"));
12014         
12015         this.footer.dataSource = this.store
12016         this.footer.container = fctr;
12017         this.footer = Roo.factory(this.footer, Roo);
12018         fctr.insertFirst(this.el);
12019         
12020         // this is a bit insane - as the paging toolbar seems to detach the el..
12021 //        dom.parentNode.parentNode.parentNode
12022          // they get detached?
12023     }
12024     
12025     
12026     Roo.View.superclass.constructor.call(this);
12027     
12028     
12029 };
12030
12031 Roo.extend(Roo.View, Roo.util.Observable, {
12032     
12033      /**
12034      * @cfg {Roo.data.Store} store Data store to load data from.
12035      */
12036     store : false,
12037     
12038     /**
12039      * @cfg {String|Roo.Element} el The container element.
12040      */
12041     el : '',
12042     
12043     /**
12044      * @cfg {String|Roo.Template} tpl The template used by this View 
12045      */
12046     tpl : false,
12047     /**
12048      * @cfg {String} dataName the named area of the template to use as the data area
12049      *                          Works with domtemplates roo-name="name"
12050      */
12051     dataName: false,
12052     /**
12053      * @cfg {String} selectedClass The css class to add to selected nodes
12054      */
12055     selectedClass : "x-view-selected",
12056      /**
12057      * @cfg {String} emptyText The empty text to show when nothing is loaded.
12058      */
12059     emptyText : "",
12060     
12061     /**
12062      * @cfg {String} text to display on mask (default Loading)
12063      */
12064     mask : false,
12065     /**
12066      * @cfg {Boolean} multiSelect Allow multiple selection
12067      */
12068     multiSelect : false,
12069     /**
12070      * @cfg {Boolean} singleSelect Allow single selection
12071      */
12072     singleSelect:  false,
12073     
12074     /**
12075      * @cfg {Boolean} toggleSelect - selecting 
12076      */
12077     toggleSelect : false,
12078     
12079     /**
12080      * @cfg {Boolean} tickable - selecting 
12081      */
12082     tickable : false,
12083     
12084     /**
12085      * Returns the element this view is bound to.
12086      * @return {Roo.Element}
12087      */
12088     getEl : function(){
12089         return this.wrapEl;
12090     },
12091     
12092     
12093
12094     /**
12095      * Refreshes the view. - called by datachanged on the store. - do not call directly.
12096      */
12097     refresh : function(){
12098         Roo.log('refresh');
12099         var t = this.tpl;
12100         
12101         // if we are using something like 'domtemplate', then
12102         // the what gets used is:
12103         // t.applySubtemplate(NAME, data, wrapping data..)
12104         // the outer template then get' applied with
12105         //     the store 'extra data'
12106         // and the body get's added to the
12107         //      roo-name="data" node?
12108         //      <span class='roo-tpl-{name}'></span> ?????
12109         
12110         
12111         
12112         this.clearSelections();
12113         this.el.update("");
12114         var html = [];
12115         var records = this.store.getRange();
12116         if(records.length < 1) {
12117             
12118             // is this valid??  = should it render a template??
12119             
12120             this.el.update(this.emptyText);
12121             return;
12122         }
12123         var el = this.el;
12124         if (this.dataName) {
12125             this.el.update(t.apply(this.store.meta)); //????
12126             el = this.el.child('.roo-tpl-' + this.dataName);
12127         }
12128         
12129         for(var i = 0, len = records.length; i < len; i++){
12130             var data = this.prepareData(records[i].data, i, records[i]);
12131             this.fireEvent("preparedata", this, data, i, records[i]);
12132             
12133             var d = Roo.apply({}, data);
12134             
12135             if(this.tickable){
12136                 Roo.apply(d, {'roo-id' : Roo.id()});
12137                 
12138                 var _this = this;
12139             
12140                 Roo.each(this.parent.item, function(item){
12141                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
12142                         return;
12143                     }
12144                     Roo.apply(d, {'roo-data-checked' : 'checked'});
12145                 });
12146             }
12147             
12148             html[html.length] = Roo.util.Format.trim(
12149                 this.dataName ?
12150                     t.applySubtemplate(this.dataName, d, this.store.meta) :
12151                     t.apply(d)
12152             );
12153         }
12154         
12155         
12156         
12157         el.update(html.join(""));
12158         this.nodes = el.dom.childNodes;
12159         this.updateIndexes(0);
12160     },
12161     
12162
12163     /**
12164      * Function to override to reformat the data that is sent to
12165      * the template for each node.
12166      * DEPRICATED - use the preparedata event handler.
12167      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
12168      * a JSON object for an UpdateManager bound view).
12169      */
12170     prepareData : function(data, index, record)
12171     {
12172         this.fireEvent("preparedata", this, data, index, record);
12173         return data;
12174     },
12175
12176     onUpdate : function(ds, record){
12177          Roo.log('on update');   
12178         this.clearSelections();
12179         var index = this.store.indexOf(record);
12180         var n = this.nodes[index];
12181         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
12182         n.parentNode.removeChild(n);
12183         this.updateIndexes(index, index);
12184     },
12185
12186     
12187     
12188 // --------- FIXME     
12189     onAdd : function(ds, records, index)
12190     {
12191         Roo.log(['on Add', ds, records, index] );        
12192         this.clearSelections();
12193         if(this.nodes.length == 0){
12194             this.refresh();
12195             return;
12196         }
12197         var n = this.nodes[index];
12198         for(var i = 0, len = records.length; i < len; i++){
12199             var d = this.prepareData(records[i].data, i, records[i]);
12200             if(n){
12201                 this.tpl.insertBefore(n, d);
12202             }else{
12203                 
12204                 this.tpl.append(this.el, d);
12205             }
12206         }
12207         this.updateIndexes(index);
12208     },
12209
12210     onRemove : function(ds, record, index){
12211         Roo.log('onRemove');
12212         this.clearSelections();
12213         var el = this.dataName  ?
12214             this.el.child('.roo-tpl-' + this.dataName) :
12215             this.el; 
12216         
12217         el.dom.removeChild(this.nodes[index]);
12218         this.updateIndexes(index);
12219     },
12220
12221     /**
12222      * Refresh an individual node.
12223      * @param {Number} index
12224      */
12225     refreshNode : function(index){
12226         this.onUpdate(this.store, this.store.getAt(index));
12227     },
12228
12229     updateIndexes : function(startIndex, endIndex){
12230         var ns = this.nodes;
12231         startIndex = startIndex || 0;
12232         endIndex = endIndex || ns.length - 1;
12233         for(var i = startIndex; i <= endIndex; i++){
12234             ns[i].nodeIndex = i;
12235         }
12236     },
12237
12238     /**
12239      * Changes the data store this view uses and refresh the view.
12240      * @param {Store} store
12241      */
12242     setStore : function(store, initial){
12243         if(!initial && this.store){
12244             this.store.un("datachanged", this.refresh);
12245             this.store.un("add", this.onAdd);
12246             this.store.un("remove", this.onRemove);
12247             this.store.un("update", this.onUpdate);
12248             this.store.un("clear", this.refresh);
12249             this.store.un("beforeload", this.onBeforeLoad);
12250             this.store.un("load", this.onLoad);
12251             this.store.un("loadexception", this.onLoad);
12252         }
12253         if(store){
12254           
12255             store.on("datachanged", this.refresh, this);
12256             store.on("add", this.onAdd, this);
12257             store.on("remove", this.onRemove, this);
12258             store.on("update", this.onUpdate, this);
12259             store.on("clear", this.refresh, this);
12260             store.on("beforeload", this.onBeforeLoad, this);
12261             store.on("load", this.onLoad, this);
12262             store.on("loadexception", this.onLoad, this);
12263         }
12264         
12265         if(store){
12266             this.refresh();
12267         }
12268     },
12269     /**
12270      * onbeforeLoad - masks the loading area.
12271      *
12272      */
12273     onBeforeLoad : function(store,opts)
12274     {
12275          Roo.log('onBeforeLoad');   
12276         if (!opts.add) {
12277             this.el.update("");
12278         }
12279         this.el.mask(this.mask ? this.mask : "Loading" ); 
12280     },
12281     onLoad : function ()
12282     {
12283         this.el.unmask();
12284     },
12285     
12286
12287     /**
12288      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
12289      * @param {HTMLElement} node
12290      * @return {HTMLElement} The template node
12291      */
12292     findItemFromChild : function(node){
12293         var el = this.dataName  ?
12294             this.el.child('.roo-tpl-' + this.dataName,true) :
12295             this.el.dom; 
12296         
12297         if(!node || node.parentNode == el){
12298                     return node;
12299             }
12300             var p = node.parentNode;
12301             while(p && p != el){
12302             if(p.parentNode == el){
12303                 return p;
12304             }
12305             p = p.parentNode;
12306         }
12307             return null;
12308     },
12309
12310     /** @ignore */
12311     onClick : function(e){
12312         var item = this.findItemFromChild(e.getTarget());
12313         if(item){
12314             var index = this.indexOf(item);
12315             if(this.onItemClick(item, index, e) !== false){
12316                 this.fireEvent("click", this, index, item, e);
12317             }
12318         }else{
12319             this.clearSelections();
12320         }
12321     },
12322
12323     /** @ignore */
12324     onContextMenu : function(e){
12325         var item = this.findItemFromChild(e.getTarget());
12326         if(item){
12327             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
12328         }
12329     },
12330
12331     /** @ignore */
12332     onDblClick : function(e){
12333         var item = this.findItemFromChild(e.getTarget());
12334         if(item){
12335             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
12336         }
12337     },
12338
12339     onItemClick : function(item, index, e)
12340     {
12341         if(this.fireEvent("beforeclick", this, index, item, e) === false){
12342             return false;
12343         }
12344         if (this.toggleSelect) {
12345             var m = this.isSelected(item) ? 'unselect' : 'select';
12346             Roo.log(m);
12347             var _t = this;
12348             _t[m](item, true, false);
12349             return true;
12350         }
12351         if(this.multiSelect || this.singleSelect){
12352             if(this.multiSelect && e.shiftKey && this.lastSelection){
12353                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
12354             }else{
12355                 this.select(item, this.multiSelect && e.ctrlKey);
12356                 this.lastSelection = item;
12357             }
12358             
12359             if(!this.tickable){
12360                 e.preventDefault();
12361             }
12362             
12363         }
12364         return true;
12365     },
12366
12367     /**
12368      * Get the number of selected nodes.
12369      * @return {Number}
12370      */
12371     getSelectionCount : function(){
12372         return this.selections.length;
12373     },
12374
12375     /**
12376      * Get the currently selected nodes.
12377      * @return {Array} An array of HTMLElements
12378      */
12379     getSelectedNodes : function(){
12380         return this.selections;
12381     },
12382
12383     /**
12384      * Get the indexes of the selected nodes.
12385      * @return {Array}
12386      */
12387     getSelectedIndexes : function(){
12388         var indexes = [], s = this.selections;
12389         for(var i = 0, len = s.length; i < len; i++){
12390             indexes.push(s[i].nodeIndex);
12391         }
12392         return indexes;
12393     },
12394
12395     /**
12396      * Clear all selections
12397      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
12398      */
12399     clearSelections : function(suppressEvent){
12400         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
12401             this.cmp.elements = this.selections;
12402             this.cmp.removeClass(this.selectedClass);
12403             this.selections = [];
12404             if(!suppressEvent){
12405                 this.fireEvent("selectionchange", this, this.selections);
12406             }
12407         }
12408     },
12409
12410     /**
12411      * Returns true if the passed node is selected
12412      * @param {HTMLElement/Number} node The node or node index
12413      * @return {Boolean}
12414      */
12415     isSelected : function(node){
12416         var s = this.selections;
12417         if(s.length < 1){
12418             return false;
12419         }
12420         node = this.getNode(node);
12421         return s.indexOf(node) !== -1;
12422     },
12423
12424     /**
12425      * Selects nodes.
12426      * @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
12427      * @param {Boolean} keepExisting (optional) true to keep existing selections
12428      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12429      */
12430     select : function(nodeInfo, keepExisting, suppressEvent){
12431         if(nodeInfo instanceof Array){
12432             if(!keepExisting){
12433                 this.clearSelections(true);
12434             }
12435             for(var i = 0, len = nodeInfo.length; i < len; i++){
12436                 this.select(nodeInfo[i], true, true);
12437             }
12438             return;
12439         } 
12440         var node = this.getNode(nodeInfo);
12441         if(!node || this.isSelected(node)){
12442             return; // already selected.
12443         }
12444         if(!keepExisting){
12445             this.clearSelections(true);
12446         }
12447         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
12448             Roo.fly(node).addClass(this.selectedClass);
12449             this.selections.push(node);
12450             if(!suppressEvent){
12451                 this.fireEvent("selectionchange", this, this.selections);
12452             }
12453         }
12454         
12455         
12456     },
12457       /**
12458      * Unselects nodes.
12459      * @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
12460      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
12461      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12462      */
12463     unselect : function(nodeInfo, keepExisting, suppressEvent)
12464     {
12465         if(nodeInfo instanceof Array){
12466             Roo.each(this.selections, function(s) {
12467                 this.unselect(s, nodeInfo);
12468             }, this);
12469             return;
12470         }
12471         var node = this.getNode(nodeInfo);
12472         if(!node || !this.isSelected(node)){
12473             Roo.log("not selected");
12474             return; // not selected.
12475         }
12476         // fireevent???
12477         var ns = [];
12478         Roo.each(this.selections, function(s) {
12479             if (s == node ) {
12480                 Roo.fly(node).removeClass(this.selectedClass);
12481
12482                 return;
12483             }
12484             ns.push(s);
12485         },this);
12486         
12487         this.selections= ns;
12488         this.fireEvent("selectionchange", this, this.selections);
12489     },
12490
12491     /**
12492      * Gets a template node.
12493      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12494      * @return {HTMLElement} The node or null if it wasn't found
12495      */
12496     getNode : function(nodeInfo){
12497         if(typeof nodeInfo == "string"){
12498             return document.getElementById(nodeInfo);
12499         }else if(typeof nodeInfo == "number"){
12500             return this.nodes[nodeInfo];
12501         }
12502         return nodeInfo;
12503     },
12504
12505     /**
12506      * Gets a range template nodes.
12507      * @param {Number} startIndex
12508      * @param {Number} endIndex
12509      * @return {Array} An array of nodes
12510      */
12511     getNodes : function(start, end){
12512         var ns = this.nodes;
12513         start = start || 0;
12514         end = typeof end == "undefined" ? ns.length - 1 : end;
12515         var nodes = [];
12516         if(start <= end){
12517             for(var i = start; i <= end; i++){
12518                 nodes.push(ns[i]);
12519             }
12520         } else{
12521             for(var i = start; i >= end; i--){
12522                 nodes.push(ns[i]);
12523             }
12524         }
12525         return nodes;
12526     },
12527
12528     /**
12529      * Finds the index of the passed node
12530      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12531      * @return {Number} The index of the node or -1
12532      */
12533     indexOf : function(node){
12534         node = this.getNode(node);
12535         if(typeof node.nodeIndex == "number"){
12536             return node.nodeIndex;
12537         }
12538         var ns = this.nodes;
12539         for(var i = 0, len = ns.length; i < len; i++){
12540             if(ns[i] == node){
12541                 return i;
12542             }
12543         }
12544         return -1;
12545     }
12546 });
12547 /*
12548  * - LGPL
12549  *
12550  * based on jquery fullcalendar
12551  * 
12552  */
12553
12554 Roo.bootstrap = Roo.bootstrap || {};
12555 /**
12556  * @class Roo.bootstrap.Calendar
12557  * @extends Roo.bootstrap.Component
12558  * Bootstrap Calendar class
12559  * @cfg {Boolean} loadMask (true|false) default false
12560  * @cfg {Object} header generate the user specific header of the calendar, default false
12561
12562  * @constructor
12563  * Create a new Container
12564  * @param {Object} config The config object
12565  */
12566
12567
12568
12569 Roo.bootstrap.Calendar = function(config){
12570     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
12571      this.addEvents({
12572         /**
12573              * @event select
12574              * Fires when a date is selected
12575              * @param {DatePicker} this
12576              * @param {Date} date The selected date
12577              */
12578         'select': true,
12579         /**
12580              * @event monthchange
12581              * Fires when the displayed month changes 
12582              * @param {DatePicker} this
12583              * @param {Date} date The selected month
12584              */
12585         'monthchange': true,
12586         /**
12587              * @event evententer
12588              * Fires when mouse over an event
12589              * @param {Calendar} this
12590              * @param {event} Event
12591              */
12592         'evententer': true,
12593         /**
12594              * @event eventleave
12595              * Fires when the mouse leaves an
12596              * @param {Calendar} this
12597              * @param {event}
12598              */
12599         'eventleave': true,
12600         /**
12601              * @event eventclick
12602              * Fires when the mouse click an
12603              * @param {Calendar} this
12604              * @param {event}
12605              */
12606         'eventclick': true
12607         
12608     });
12609
12610 };
12611
12612 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
12613     
12614      /**
12615      * @cfg {Number} startDay
12616      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
12617      */
12618     startDay : 0,
12619     
12620     loadMask : false,
12621     
12622     header : false,
12623       
12624     getAutoCreate : function(){
12625         
12626         
12627         var fc_button = function(name, corner, style, content ) {
12628             return Roo.apply({},{
12629                 tag : 'span',
12630                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
12631                          (corner.length ?
12632                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
12633                             ''
12634                         ),
12635                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
12636                 unselectable: 'on'
12637             });
12638         };
12639         
12640         var header = {};
12641         
12642         if(!this.header){
12643             header = {
12644                 tag : 'table',
12645                 cls : 'fc-header',
12646                 style : 'width:100%',
12647                 cn : [
12648                     {
12649                         tag: 'tr',
12650                         cn : [
12651                             {
12652                                 tag : 'td',
12653                                 cls : 'fc-header-left',
12654                                 cn : [
12655                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
12656                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
12657                                     { tag: 'span', cls: 'fc-header-space' },
12658                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
12659
12660
12661                                 ]
12662                             },
12663
12664                             {
12665                                 tag : 'td',
12666                                 cls : 'fc-header-center',
12667                                 cn : [
12668                                     {
12669                                         tag: 'span',
12670                                         cls: 'fc-header-title',
12671                                         cn : {
12672                                             tag: 'H2',
12673                                             html : 'month / year'
12674                                         }
12675                                     }
12676
12677                                 ]
12678                             },
12679                             {
12680                                 tag : 'td',
12681                                 cls : 'fc-header-right',
12682                                 cn : [
12683                               /*      fc_button('month', 'left', '', 'month' ),
12684                                     fc_button('week', '', '', 'week' ),
12685                                     fc_button('day', 'right', '', 'day' )
12686                                 */    
12687
12688                                 ]
12689                             }
12690
12691                         ]
12692                     }
12693                 ]
12694             };
12695         }
12696         
12697         header = this.header;
12698         
12699        
12700         var cal_heads = function() {
12701             var ret = [];
12702             // fixme - handle this.
12703             
12704             for (var i =0; i < Date.dayNames.length; i++) {
12705                 var d = Date.dayNames[i];
12706                 ret.push({
12707                     tag: 'th',
12708                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
12709                     html : d.substring(0,3)
12710                 });
12711                 
12712             }
12713             ret[0].cls += ' fc-first';
12714             ret[6].cls += ' fc-last';
12715             return ret;
12716         };
12717         var cal_cell = function(n) {
12718             return  {
12719                 tag: 'td',
12720                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
12721                 cn : [
12722                     {
12723                         cn : [
12724                             {
12725                                 cls: 'fc-day-number',
12726                                 html: 'D'
12727                             },
12728                             {
12729                                 cls: 'fc-day-content',
12730                              
12731                                 cn : [
12732                                      {
12733                                         style: 'position: relative;' // height: 17px;
12734                                     }
12735                                 ]
12736                             }
12737                             
12738                             
12739                         ]
12740                     }
12741                 ]
12742                 
12743             }
12744         };
12745         var cal_rows = function() {
12746             
12747             var ret = []
12748             for (var r = 0; r < 6; r++) {
12749                 var row= {
12750                     tag : 'tr',
12751                     cls : 'fc-week',
12752                     cn : []
12753                 };
12754                 
12755                 for (var i =0; i < Date.dayNames.length; i++) {
12756                     var d = Date.dayNames[i];
12757                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
12758
12759                 }
12760                 row.cn[0].cls+=' fc-first';
12761                 row.cn[0].cn[0].style = 'min-height:90px';
12762                 row.cn[6].cls+=' fc-last';
12763                 ret.push(row);
12764                 
12765             }
12766             ret[0].cls += ' fc-first';
12767             ret[4].cls += ' fc-prev-last';
12768             ret[5].cls += ' fc-last';
12769             return ret;
12770             
12771         };
12772         
12773         var cal_table = {
12774             tag: 'table',
12775             cls: 'fc-border-separate',
12776             style : 'width:100%',
12777             cellspacing  : 0,
12778             cn : [
12779                 { 
12780                     tag: 'thead',
12781                     cn : [
12782                         { 
12783                             tag: 'tr',
12784                             cls : 'fc-first fc-last',
12785                             cn : cal_heads()
12786                         }
12787                     ]
12788                 },
12789                 { 
12790                     tag: 'tbody',
12791                     cn : cal_rows()
12792                 }
12793                   
12794             ]
12795         };
12796          
12797          var cfg = {
12798             cls : 'fc fc-ltr',
12799             cn : [
12800                 header,
12801                 {
12802                     cls : 'fc-content',
12803                     style : "position: relative;",
12804                     cn : [
12805                         {
12806                             cls : 'fc-view fc-view-month fc-grid',
12807                             style : 'position: relative',
12808                             unselectable : 'on',
12809                             cn : [
12810                                 {
12811                                     cls : 'fc-event-container',
12812                                     style : 'position:absolute;z-index:8;top:0;left:0;'
12813                                 },
12814                                 cal_table
12815                             ]
12816                         }
12817                     ]
12818     
12819                 }
12820            ] 
12821             
12822         };
12823         
12824          
12825         
12826         return cfg;
12827     },
12828     
12829     
12830     initEvents : function()
12831     {
12832         if(!this.store){
12833             throw "can not find store for calendar";
12834         }
12835         
12836         var mark = {
12837             tag: "div",
12838             cls:"x-dlg-mask",
12839             style: "text-align:center",
12840             cn: [
12841                 {
12842                     tag: "div",
12843                     style: "background-color:white;width:50%;margin:250 auto",
12844                     cn: [
12845                         {
12846                             tag: "img",
12847                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
12848                         },
12849                         {
12850                             tag: "span",
12851                             html: "Loading"
12852                         }
12853                         
12854                     ]
12855                 }
12856             ]
12857         }
12858         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
12859         
12860         var size = this.el.select('.fc-content', true).first().getSize();
12861         this.maskEl.setSize(size.width, size.height);
12862         this.maskEl.enableDisplayMode("block");
12863         if(!this.loadMask){
12864             this.maskEl.hide();
12865         }
12866         
12867         this.store = Roo.factory(this.store, Roo.data);
12868         this.store.on('load', this.onLoad, this);
12869         this.store.on('beforeload', this.onBeforeLoad, this);
12870         
12871         this.resize();
12872         
12873         this.cells = this.el.select('.fc-day',true);
12874         //Roo.log(this.cells);
12875         this.textNodes = this.el.query('.fc-day-number');
12876         this.cells.addClassOnOver('fc-state-hover');
12877         
12878         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
12879         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
12880         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
12881         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
12882         
12883         this.on('monthchange', this.onMonthChange, this);
12884         
12885         this.update(new Date().clearTime());
12886     },
12887     
12888     resize : function() {
12889         var sz  = this.el.getSize();
12890         
12891         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
12892         this.el.select('.fc-day-content div',true).setHeight(34);
12893     },
12894     
12895     
12896     // private
12897     showPrevMonth : function(e){
12898         this.update(this.activeDate.add("mo", -1));
12899     },
12900     showToday : function(e){
12901         this.update(new Date().clearTime());
12902     },
12903     // private
12904     showNextMonth : function(e){
12905         this.update(this.activeDate.add("mo", 1));
12906     },
12907
12908     // private
12909     showPrevYear : function(){
12910         this.update(this.activeDate.add("y", -1));
12911     },
12912
12913     // private
12914     showNextYear : function(){
12915         this.update(this.activeDate.add("y", 1));
12916     },
12917
12918     
12919    // private
12920     update : function(date)
12921     {
12922         var vd = this.activeDate;
12923         this.activeDate = date;
12924 //        if(vd && this.el){
12925 //            var t = date.getTime();
12926 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
12927 //                Roo.log('using add remove');
12928 //                
12929 //                this.fireEvent('monthchange', this, date);
12930 //                
12931 //                this.cells.removeClass("fc-state-highlight");
12932 //                this.cells.each(function(c){
12933 //                   if(c.dateValue == t){
12934 //                       c.addClass("fc-state-highlight");
12935 //                       setTimeout(function(){
12936 //                            try{c.dom.firstChild.focus();}catch(e){}
12937 //                       }, 50);
12938 //                       return false;
12939 //                   }
12940 //                   return true;
12941 //                });
12942 //                return;
12943 //            }
12944 //        }
12945         
12946         var days = date.getDaysInMonth();
12947         
12948         var firstOfMonth = date.getFirstDateOfMonth();
12949         var startingPos = firstOfMonth.getDay()-this.startDay;
12950         
12951         if(startingPos < this.startDay){
12952             startingPos += 7;
12953         }
12954         
12955         var pm = date.add(Date.MONTH, -1);
12956         var prevStart = pm.getDaysInMonth()-startingPos;
12957 //        
12958         this.cells = this.el.select('.fc-day',true);
12959         this.textNodes = this.el.query('.fc-day-number');
12960         this.cells.addClassOnOver('fc-state-hover');
12961         
12962         var cells = this.cells.elements;
12963         var textEls = this.textNodes;
12964         
12965         Roo.each(cells, function(cell){
12966             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
12967         });
12968         
12969         days += startingPos;
12970
12971         // convert everything to numbers so it's fast
12972         var day = 86400000;
12973         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
12974         //Roo.log(d);
12975         //Roo.log(pm);
12976         //Roo.log(prevStart);
12977         
12978         var today = new Date().clearTime().getTime();
12979         var sel = date.clearTime().getTime();
12980         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
12981         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
12982         var ddMatch = this.disabledDatesRE;
12983         var ddText = this.disabledDatesText;
12984         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
12985         var ddaysText = this.disabledDaysText;
12986         var format = this.format;
12987         
12988         var setCellClass = function(cal, cell){
12989             cell.row = 0;
12990             cell.events = [];
12991             cell.more = [];
12992             //Roo.log('set Cell Class');
12993             cell.title = "";
12994             var t = d.getTime();
12995             
12996             //Roo.log(d);
12997             
12998             cell.dateValue = t;
12999             if(t == today){
13000                 cell.className += " fc-today";
13001                 cell.className += " fc-state-highlight";
13002                 cell.title = cal.todayText;
13003             }
13004             if(t == sel){
13005                 // disable highlight in other month..
13006                 //cell.className += " fc-state-highlight";
13007                 
13008             }
13009             // disabling
13010             if(t < min) {
13011                 cell.className = " fc-state-disabled";
13012                 cell.title = cal.minText;
13013                 return;
13014             }
13015             if(t > max) {
13016                 cell.className = " fc-state-disabled";
13017                 cell.title = cal.maxText;
13018                 return;
13019             }
13020             if(ddays){
13021                 if(ddays.indexOf(d.getDay()) != -1){
13022                     cell.title = ddaysText;
13023                     cell.className = " fc-state-disabled";
13024                 }
13025             }
13026             if(ddMatch && format){
13027                 var fvalue = d.dateFormat(format);
13028                 if(ddMatch.test(fvalue)){
13029                     cell.title = ddText.replace("%0", fvalue);
13030                     cell.className = " fc-state-disabled";
13031                 }
13032             }
13033             
13034             if (!cell.initialClassName) {
13035                 cell.initialClassName = cell.dom.className;
13036             }
13037             
13038             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
13039         };
13040
13041         var i = 0;
13042         
13043         for(; i < startingPos; i++) {
13044             textEls[i].innerHTML = (++prevStart);
13045             d.setDate(d.getDate()+1);
13046             
13047             cells[i].className = "fc-past fc-other-month";
13048             setCellClass(this, cells[i]);
13049         }
13050         
13051         var intDay = 0;
13052         
13053         for(; i < days; i++){
13054             intDay = i - startingPos + 1;
13055             textEls[i].innerHTML = (intDay);
13056             d.setDate(d.getDate()+1);
13057             
13058             cells[i].className = ''; // "x-date-active";
13059             setCellClass(this, cells[i]);
13060         }
13061         var extraDays = 0;
13062         
13063         for(; i < 42; i++) {
13064             textEls[i].innerHTML = (++extraDays);
13065             d.setDate(d.getDate()+1);
13066             
13067             cells[i].className = "fc-future fc-other-month";
13068             setCellClass(this, cells[i]);
13069         }
13070         
13071         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
13072         
13073         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
13074         
13075         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
13076         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
13077         
13078         if(totalRows != 6){
13079             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
13080             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
13081         }
13082         
13083         this.fireEvent('monthchange', this, date);
13084         
13085         
13086         /*
13087         if(!this.internalRender){
13088             var main = this.el.dom.firstChild;
13089             var w = main.offsetWidth;
13090             this.el.setWidth(w + this.el.getBorderWidth("lr"));
13091             Roo.fly(main).setWidth(w);
13092             this.internalRender = true;
13093             // opera does not respect the auto grow header center column
13094             // then, after it gets a width opera refuses to recalculate
13095             // without a second pass
13096             if(Roo.isOpera && !this.secondPass){
13097                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
13098                 this.secondPass = true;
13099                 this.update.defer(10, this, [date]);
13100             }
13101         }
13102         */
13103         
13104     },
13105     
13106     findCell : function(dt) {
13107         dt = dt.clearTime().getTime();
13108         var ret = false;
13109         this.cells.each(function(c){
13110             //Roo.log("check " +c.dateValue + '?=' + dt);
13111             if(c.dateValue == dt){
13112                 ret = c;
13113                 return false;
13114             }
13115             return true;
13116         });
13117         
13118         return ret;
13119     },
13120     
13121     findCells : function(ev) {
13122         var s = ev.start.clone().clearTime().getTime();
13123        // Roo.log(s);
13124         var e= ev.end.clone().clearTime().getTime();
13125        // Roo.log(e);
13126         var ret = [];
13127         this.cells.each(function(c){
13128              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
13129             
13130             if(c.dateValue > e){
13131                 return ;
13132             }
13133             if(c.dateValue < s){
13134                 return ;
13135             }
13136             ret.push(c);
13137         });
13138         
13139         return ret;    
13140     },
13141     
13142 //    findBestRow: function(cells)
13143 //    {
13144 //        var ret = 0;
13145 //        
13146 //        for (var i =0 ; i < cells.length;i++) {
13147 //            ret  = Math.max(cells[i].rows || 0,ret);
13148 //        }
13149 //        return ret;
13150 //        
13151 //    },
13152     
13153     
13154     addItem : function(ev)
13155     {
13156         // look for vertical location slot in
13157         var cells = this.findCells(ev);
13158         
13159 //        ev.row = this.findBestRow(cells);
13160         
13161         // work out the location.
13162         
13163         var crow = false;
13164         var rows = [];
13165         for(var i =0; i < cells.length; i++) {
13166             
13167             cells[i].row = cells[0].row;
13168             
13169             if(i == 0){
13170                 cells[i].row = cells[i].row + 1;
13171             }
13172             
13173             if (!crow) {
13174                 crow = {
13175                     start : cells[i],
13176                     end :  cells[i]
13177                 };
13178                 continue;
13179             }
13180             if (crow.start.getY() == cells[i].getY()) {
13181                 // on same row.
13182                 crow.end = cells[i];
13183                 continue;
13184             }
13185             // different row.
13186             rows.push(crow);
13187             crow = {
13188                 start: cells[i],
13189                 end : cells[i]
13190             };
13191             
13192         }
13193         
13194         rows.push(crow);
13195         ev.els = [];
13196         ev.rows = rows;
13197         ev.cells = cells;
13198         
13199         cells[0].events.push(ev);
13200         
13201         this.calevents.push(ev);
13202     },
13203     
13204     clearEvents: function() {
13205         
13206         if(!this.calevents){
13207             return;
13208         }
13209         
13210         Roo.each(this.cells.elements, function(c){
13211             c.row = 0;
13212             c.events = [];
13213             c.more = [];
13214         });
13215         
13216         Roo.each(this.calevents, function(e) {
13217             Roo.each(e.els, function(el) {
13218                 el.un('mouseenter' ,this.onEventEnter, this);
13219                 el.un('mouseleave' ,this.onEventLeave, this);
13220                 el.remove();
13221             },this);
13222         },this);
13223         
13224         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
13225             e.remove();
13226         });
13227         
13228     },
13229     
13230     renderEvents: function()
13231     {   
13232         var _this = this;
13233         
13234         this.cells.each(function(c) {
13235             
13236             if(c.row < 5){
13237                 return;
13238             }
13239             
13240             var ev = c.events;
13241             
13242             var r = 4;
13243             if(c.row != c.events.length){
13244                 r = 4 - (4 - (c.row - c.events.length));
13245             }
13246             
13247             c.events = ev.slice(0, r);
13248             c.more = ev.slice(r);
13249             
13250             if(c.more.length && c.more.length == 1){
13251                 c.events.push(c.more.pop());
13252             }
13253             
13254             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
13255             
13256         });
13257             
13258         this.cells.each(function(c) {
13259             
13260             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
13261             
13262             
13263             for (var e = 0; e < c.events.length; e++){
13264                 var ev = c.events[e];
13265                 var rows = ev.rows;
13266                 
13267                 for(var i = 0; i < rows.length; i++) {
13268                 
13269                     // how many rows should it span..
13270
13271                     var  cfg = {
13272                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
13273                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
13274
13275                         unselectable : "on",
13276                         cn : [
13277                             {
13278                                 cls: 'fc-event-inner',
13279                                 cn : [
13280     //                                {
13281     //                                  tag:'span',
13282     //                                  cls: 'fc-event-time',
13283     //                                  html : cells.length > 1 ? '' : ev.time
13284     //                                },
13285                                     {
13286                                       tag:'span',
13287                                       cls: 'fc-event-title',
13288                                       html : String.format('{0}', ev.title)
13289                                     }
13290
13291
13292                                 ]
13293                             },
13294                             {
13295                                 cls: 'ui-resizable-handle ui-resizable-e',
13296                                 html : '&nbsp;&nbsp;&nbsp'
13297                             }
13298
13299                         ]
13300                     };
13301
13302                     if (i == 0) {
13303                         cfg.cls += ' fc-event-start';
13304                     }
13305                     if ((i+1) == rows.length) {
13306                         cfg.cls += ' fc-event-end';
13307                     }
13308
13309                     var ctr = _this.el.select('.fc-event-container',true).first();
13310                     var cg = ctr.createChild(cfg);
13311
13312                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
13313                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
13314
13315                     var r = (c.more.length) ? 1 : 0;
13316                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
13317                     cg.setWidth(ebox.right - sbox.x -2);
13318
13319                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
13320                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
13321                     cg.on('click', _this.onEventClick, _this, ev);
13322
13323                     ev.els.push(cg);
13324                     
13325                 }
13326                 
13327             }
13328             
13329             
13330             if(c.more.length){
13331                 var  cfg = {
13332                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
13333                     style : 'position: absolute',
13334                     unselectable : "on",
13335                     cn : [
13336                         {
13337                             cls: 'fc-event-inner',
13338                             cn : [
13339                                 {
13340                                   tag:'span',
13341                                   cls: 'fc-event-title',
13342                                   html : 'More'
13343                                 }
13344
13345
13346                             ]
13347                         },
13348                         {
13349                             cls: 'ui-resizable-handle ui-resizable-e',
13350                             html : '&nbsp;&nbsp;&nbsp'
13351                         }
13352
13353                     ]
13354                 };
13355
13356                 var ctr = _this.el.select('.fc-event-container',true).first();
13357                 var cg = ctr.createChild(cfg);
13358
13359                 var sbox = c.select('.fc-day-content',true).first().getBox();
13360                 var ebox = c.select('.fc-day-content',true).first().getBox();
13361                 //Roo.log(cg);
13362                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
13363                 cg.setWidth(ebox.right - sbox.x -2);
13364
13365                 cg.on('click', _this.onMoreEventClick, _this, c.more);
13366                 
13367             }
13368             
13369         });
13370         
13371         
13372         
13373     },
13374     
13375     onEventEnter: function (e, el,event,d) {
13376         this.fireEvent('evententer', this, el, event);
13377     },
13378     
13379     onEventLeave: function (e, el,event,d) {
13380         this.fireEvent('eventleave', this, el, event);
13381     },
13382     
13383     onEventClick: function (e, el,event,d) {
13384         this.fireEvent('eventclick', this, el, event);
13385     },
13386     
13387     onMonthChange: function () {
13388         this.store.load();
13389     },
13390     
13391     onMoreEventClick: function(e, el, more)
13392     {
13393         var _this = this;
13394         
13395         this.calpopover.placement = 'right';
13396         this.calpopover.setTitle('More');
13397         
13398         this.calpopover.setContent('');
13399         
13400         var ctr = this.calpopover.el.select('.popover-content', true).first();
13401         
13402         Roo.each(more, function(m){
13403             var cfg = {
13404                 cls : 'fc-event-hori fc-event-draggable',
13405                 html : m.title
13406             }
13407             var cg = ctr.createChild(cfg);
13408             
13409             cg.on('click', _this.onEventClick, _this, m);
13410         });
13411         
13412         this.calpopover.show(el);
13413         
13414         
13415     },
13416     
13417     onLoad: function () 
13418     {   
13419         this.calevents = [];
13420         var cal = this;
13421         
13422         if(this.store.getCount() > 0){
13423             this.store.data.each(function(d){
13424                cal.addItem({
13425                     id : d.data.id,
13426                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
13427                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
13428                     time : d.data.start_time,
13429                     title : d.data.title,
13430                     description : d.data.description,
13431                     venue : d.data.venue
13432                 });
13433             });
13434         }
13435         
13436         this.renderEvents();
13437         
13438         if(this.calevents.length && this.loadMask){
13439             this.maskEl.hide();
13440         }
13441     },
13442     
13443     onBeforeLoad: function()
13444     {
13445         this.clearEvents();
13446         if(this.loadMask){
13447             this.maskEl.show();
13448         }
13449     }
13450 });
13451
13452  
13453  /*
13454  * - LGPL
13455  *
13456  * element
13457  * 
13458  */
13459
13460 /**
13461  * @class Roo.bootstrap.Popover
13462  * @extends Roo.bootstrap.Component
13463  * Bootstrap Popover class
13464  * @cfg {String} html contents of the popover   (or false to use children..)
13465  * @cfg {String} title of popover (or false to hide)
13466  * @cfg {String} placement how it is placed
13467  * @cfg {String} trigger click || hover (or false to trigger manually)
13468  * @cfg {String} over what (parent or false to trigger manually.)
13469  * 
13470  * @constructor
13471  * Create a new Popover
13472  * @param {Object} config The config object
13473  */
13474
13475 Roo.bootstrap.Popover = function(config){
13476     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
13477 };
13478
13479 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
13480     
13481     title: 'Fill in a title',
13482     html: false,
13483     
13484     placement : 'right',
13485     trigger : 'hover', // hover
13486     
13487     over: 'parent',
13488     
13489     can_build_overlaid : false,
13490     
13491     getChildContainer : function()
13492     {
13493         return this.el.select('.popover-content',true).first();
13494     },
13495     
13496     getAutoCreate : function(){
13497          Roo.log('make popover?');
13498         var cfg = {
13499            cls : 'popover roo-dynamic',
13500            style: 'display:block',
13501            cn : [
13502                 {
13503                     cls : 'arrow'
13504                 },
13505                 {
13506                     cls : 'popover-inner',
13507                     cn : [
13508                         {
13509                             tag: 'h3',
13510                             cls: 'popover-title',
13511                             html : this.title
13512                         },
13513                         {
13514                             cls : 'popover-content',
13515                             html : this.html
13516                         }
13517                     ]
13518                     
13519                 }
13520            ]
13521         };
13522         
13523         return cfg;
13524     },
13525     setTitle: function(str)
13526     {
13527         this.el.select('.popover-title',true).first().dom.innerHTML = str;
13528     },
13529     setContent: function(str)
13530     {
13531         this.el.select('.popover-content',true).first().dom.innerHTML = str;
13532     },
13533     // as it get's added to the bottom of the page.
13534     onRender : function(ct, position)
13535     {
13536         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
13537         if(!this.el){
13538             var cfg = Roo.apply({},  this.getAutoCreate());
13539             cfg.id = Roo.id();
13540             
13541             if (this.cls) {
13542                 cfg.cls += ' ' + this.cls;
13543             }
13544             if (this.style) {
13545                 cfg.style = this.style;
13546             }
13547             Roo.log("adding to ")
13548             this.el = Roo.get(document.body).createChild(cfg, position);
13549             Roo.log(this.el);
13550         }
13551         this.initEvents();
13552     },
13553     
13554     initEvents : function()
13555     {
13556         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
13557         this.el.enableDisplayMode('block');
13558         this.el.hide();
13559         if (this.over === false) {
13560             return; 
13561         }
13562         if (this.triggers === false) {
13563             return;
13564         }
13565         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13566         var triggers = this.trigger ? this.trigger.split(' ') : [];
13567         Roo.each(triggers, function(trigger) {
13568         
13569             if (trigger == 'click') {
13570                 on_el.on('click', this.toggle, this);
13571             } else if (trigger != 'manual') {
13572                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
13573                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
13574       
13575                 on_el.on(eventIn  ,this.enter, this);
13576                 on_el.on(eventOut, this.leave, this);
13577             }
13578         }, this);
13579         
13580     },
13581     
13582     
13583     // private
13584     timeout : null,
13585     hoverState : null,
13586     
13587     toggle : function () {
13588         this.hoverState == 'in' ? this.leave() : this.enter();
13589     },
13590     
13591     enter : function () {
13592        
13593     
13594         clearTimeout(this.timeout);
13595     
13596         this.hoverState = 'in'
13597     
13598         if (!this.delay || !this.delay.show) {
13599             this.show();
13600             return 
13601         }
13602         var _t = this;
13603         this.timeout = setTimeout(function () {
13604             if (_t.hoverState == 'in') {
13605                 _t.show();
13606             }
13607         }, this.delay.show)
13608     },
13609     leave : function() {
13610         clearTimeout(this.timeout);
13611     
13612         this.hoverState = 'out'
13613     
13614         if (!this.delay || !this.delay.hide) {
13615             this.hide();
13616             return 
13617         }
13618         var _t = this;
13619         this.timeout = setTimeout(function () {
13620             if (_t.hoverState == 'out') {
13621                 _t.hide();
13622             }
13623         }, this.delay.hide)
13624     },
13625     
13626     show : function (on_el)
13627     {
13628         if (!on_el) {
13629             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13630         }
13631         // set content.
13632         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
13633         if (this.html !== false) {
13634             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
13635         }
13636         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
13637         if (!this.title.length) {
13638             this.el.select('.popover-title',true).hide();
13639         }
13640         
13641         var placement = typeof this.placement == 'function' ?
13642             this.placement.call(this, this.el, on_el) :
13643             this.placement;
13644             
13645         var autoToken = /\s?auto?\s?/i;
13646         var autoPlace = autoToken.test(placement);
13647         if (autoPlace) {
13648             placement = placement.replace(autoToken, '') || 'top';
13649         }
13650         
13651         //this.el.detach()
13652         //this.el.setXY([0,0]);
13653         this.el.show();
13654         this.el.dom.style.display='block';
13655         this.el.addClass(placement);
13656         
13657         //this.el.appendTo(on_el);
13658         
13659         var p = this.getPosition();
13660         var box = this.el.getBox();
13661         
13662         if (autoPlace) {
13663             // fixme..
13664         }
13665         var align = Roo.bootstrap.Popover.alignment[placement]
13666         this.el.alignTo(on_el, align[0],align[1]);
13667         //var arrow = this.el.select('.arrow',true).first();
13668         //arrow.set(align[2], 
13669         
13670         this.el.addClass('in');
13671         this.hoverState = null;
13672         
13673         if (this.el.hasClass('fade')) {
13674             // fade it?
13675         }
13676         
13677     },
13678     hide : function()
13679     {
13680         this.el.setXY([0,0]);
13681         this.el.removeClass('in');
13682         this.el.hide();
13683         
13684     }
13685     
13686 });
13687
13688 Roo.bootstrap.Popover.alignment = {
13689     'left' : ['r-l', [-10,0], 'right'],
13690     'right' : ['l-r', [10,0], 'left'],
13691     'bottom' : ['t-b', [0,10], 'top'],
13692     'top' : [ 'b-t', [0,-10], 'bottom']
13693 };
13694
13695  /*
13696  * - LGPL
13697  *
13698  * Progress
13699  * 
13700  */
13701
13702 /**
13703  * @class Roo.bootstrap.Progress
13704  * @extends Roo.bootstrap.Component
13705  * Bootstrap Progress class
13706  * @cfg {Boolean} striped striped of the progress bar
13707  * @cfg {Boolean} active animated of the progress bar
13708  * 
13709  * 
13710  * @constructor
13711  * Create a new Progress
13712  * @param {Object} config The config object
13713  */
13714
13715 Roo.bootstrap.Progress = function(config){
13716     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
13717 };
13718
13719 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
13720     
13721     striped : false,
13722     active: false,
13723     
13724     getAutoCreate : function(){
13725         var cfg = {
13726             tag: 'div',
13727             cls: 'progress'
13728         };
13729         
13730         
13731         if(this.striped){
13732             cfg.cls += ' progress-striped';
13733         }
13734       
13735         if(this.active){
13736             cfg.cls += ' active';
13737         }
13738         
13739         
13740         return cfg;
13741     }
13742    
13743 });
13744
13745  
13746
13747  /*
13748  * - LGPL
13749  *
13750  * ProgressBar
13751  * 
13752  */
13753
13754 /**
13755  * @class Roo.bootstrap.ProgressBar
13756  * @extends Roo.bootstrap.Component
13757  * Bootstrap ProgressBar class
13758  * @cfg {Number} aria_valuenow aria-value now
13759  * @cfg {Number} aria_valuemin aria-value min
13760  * @cfg {Number} aria_valuemax aria-value max
13761  * @cfg {String} label label for the progress bar
13762  * @cfg {String} panel (success | info | warning | danger )
13763  * @cfg {String} role role of the progress bar
13764  * @cfg {String} sr_only text
13765  * 
13766  * 
13767  * @constructor
13768  * Create a new ProgressBar
13769  * @param {Object} config The config object
13770  */
13771
13772 Roo.bootstrap.ProgressBar = function(config){
13773     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
13774 };
13775
13776 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
13777     
13778     aria_valuenow : 0,
13779     aria_valuemin : 0,
13780     aria_valuemax : 100,
13781     label : false,
13782     panel : false,
13783     role : false,
13784     sr_only: false,
13785     
13786     getAutoCreate : function()
13787     {
13788         
13789         var cfg = {
13790             tag: 'div',
13791             cls: 'progress-bar',
13792             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
13793         };
13794         
13795         if(this.sr_only){
13796             cfg.cn = {
13797                 tag: 'span',
13798                 cls: 'sr-only',
13799                 html: this.sr_only
13800             }
13801         }
13802         
13803         if(this.role){
13804             cfg.role = this.role;
13805         }
13806         
13807         if(this.aria_valuenow){
13808             cfg['aria-valuenow'] = this.aria_valuenow;
13809         }
13810         
13811         if(this.aria_valuemin){
13812             cfg['aria-valuemin'] = this.aria_valuemin;
13813         }
13814         
13815         if(this.aria_valuemax){
13816             cfg['aria-valuemax'] = this.aria_valuemax;
13817         }
13818         
13819         if(this.label && !this.sr_only){
13820             cfg.html = this.label;
13821         }
13822         
13823         if(this.panel){
13824             cfg.cls += ' progress-bar-' + this.panel;
13825         }
13826         
13827         return cfg;
13828     },
13829     
13830     update : function(aria_valuenow)
13831     {
13832         this.aria_valuenow = aria_valuenow;
13833         
13834         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
13835     }
13836    
13837 });
13838
13839  
13840
13841  /*
13842  * - LGPL
13843  *
13844  * column
13845  * 
13846  */
13847
13848 /**
13849  * @class Roo.bootstrap.TabGroup
13850  * @extends Roo.bootstrap.Column
13851  * Bootstrap Column class
13852  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
13853  * @cfg {Boolean} carousel true to make the group behave like a carousel
13854  * 
13855  * @constructor
13856  * Create a new TabGroup
13857  * @param {Object} config The config object
13858  */
13859
13860 Roo.bootstrap.TabGroup = function(config){
13861     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
13862     if (!this.navId) {
13863         this.navId = Roo.id();
13864     }
13865     this.tabs = [];
13866     Roo.bootstrap.TabGroup.register(this);
13867     
13868 };
13869
13870 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
13871     
13872     carousel : false,
13873     transition : false,
13874      
13875     getAutoCreate : function()
13876     {
13877         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
13878         
13879         cfg.cls += ' tab-content';
13880         
13881         if (this.carousel) {
13882             cfg.cls += ' carousel slide';
13883             cfg.cn = [{
13884                cls : 'carousel-inner'
13885             }]
13886         }
13887         
13888         
13889         return cfg;
13890     },
13891     getChildContainer : function()
13892     {
13893         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
13894     },
13895     
13896     /**
13897     * register a Navigation item
13898     * @param {Roo.bootstrap.NavItem} the navitem to add
13899     */
13900     register : function(item)
13901     {
13902         this.tabs.push( item);
13903         item.navId = this.navId; // not really needed..
13904     
13905     },
13906     
13907     getActivePanel : function()
13908     {
13909         var r = false;
13910         Roo.each(this.tabs, function(t) {
13911             if (t.active) {
13912                 r = t;
13913                 return false;
13914             }
13915             return null;
13916         });
13917         return r;
13918         
13919     },
13920     getPanelByName : function(n)
13921     {
13922         var r = false;
13923         Roo.each(this.tabs, function(t) {
13924             if (t.tabId == n) {
13925                 r = t;
13926                 return false;
13927             }
13928             return null;
13929         });
13930         return r;
13931     },
13932     indexOfPanel : function(p)
13933     {
13934         var r = false;
13935         Roo.each(this.tabs, function(t,i) {
13936             if (t.tabId == p.tabId) {
13937                 r = i;
13938                 return false;
13939             }
13940             return null;
13941         });
13942         return r;
13943     },
13944     /**
13945      * show a specific panel
13946      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
13947      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
13948      */
13949     showPanel : function (pan)
13950     {
13951         
13952         if (typeof(pan) == 'number') {
13953             pan = this.tabs[pan];
13954         }
13955         if (typeof(pan) == 'string') {
13956             pan = this.getPanelByName(pan);
13957         }
13958         if (pan.tabId == this.getActivePanel().tabId) {
13959             return true;
13960         }
13961         var cur = this.getActivePanel();
13962         
13963         if (false === cur.fireEvent('beforedeactivate')) {
13964             return false;
13965         }
13966         
13967         if (this.carousel) {
13968             this.transition = true;
13969             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
13970             var lr = dir == 'next' ? 'left' : 'right';
13971             pan.el.addClass(dir); // or prev
13972             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
13973             cur.el.addClass(lr); // or right
13974             pan.el.addClass(lr);
13975             
13976             var _this = this;
13977             cur.el.on('transitionend', function() {
13978                 Roo.log("trans end?");
13979                 
13980                 pan.el.removeClass([lr,dir]);
13981                 pan.setActive(true);
13982                 
13983                 cur.el.removeClass([lr]);
13984                 cur.setActive(false);
13985                 
13986                 _this.transition = false;
13987                 
13988             }, this, { single:  true } );
13989             return true;
13990         }
13991         
13992         cur.setActive(false);
13993         pan.setActive(true);
13994         return true;
13995         
13996     },
13997     showPanelNext : function()
13998     {
13999         var i = this.indexOfPanel(this.getActivePanel());
14000         if (i > this.tabs.length) {
14001             return;
14002         }
14003         this.showPanel(this.tabs[i+1]);
14004     },
14005     showPanelPrev : function()
14006     {
14007         var i = this.indexOfPanel(this.getActivePanel());
14008         if (i  < 1) {
14009             return;
14010         }
14011         this.showPanel(this.tabs[i-1]);
14012     }
14013     
14014     
14015   
14016 });
14017
14018  
14019
14020  
14021  
14022 Roo.apply(Roo.bootstrap.TabGroup, {
14023     
14024     groups: {},
14025      /**
14026     * register a Navigation Group
14027     * @param {Roo.bootstrap.NavGroup} the navgroup to add
14028     */
14029     register : function(navgrp)
14030     {
14031         this.groups[navgrp.navId] = navgrp;
14032         
14033     },
14034     /**
14035     * fetch a Navigation Group based on the navigation ID
14036     * if one does not exist , it will get created.
14037     * @param {string} the navgroup to add
14038     * @returns {Roo.bootstrap.NavGroup} the navgroup 
14039     */
14040     get: function(navId) {
14041         if (typeof(this.groups[navId]) == 'undefined') {
14042             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
14043         }
14044         return this.groups[navId] ;
14045     }
14046     
14047     
14048     
14049 });
14050
14051  /*
14052  * - LGPL
14053  *
14054  * TabPanel
14055  * 
14056  */
14057
14058 /**
14059  * @class Roo.bootstrap.TabPanel
14060  * @extends Roo.bootstrap.Component
14061  * Bootstrap TabPanel class
14062  * @cfg {Boolean} active panel active
14063  * @cfg {String} html panel content
14064  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
14065  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
14066  * 
14067  * 
14068  * @constructor
14069  * Create a new TabPanel
14070  * @param {Object} config The config object
14071  */
14072
14073 Roo.bootstrap.TabPanel = function(config){
14074     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
14075     this.addEvents({
14076         /**
14077              * @event changed
14078              * Fires when the active status changes
14079              * @param {Roo.bootstrap.TabPanel} this
14080              * @param {Boolean} state the new state
14081             
14082          */
14083         'changed': true,
14084         /**
14085              * @event beforedeactivate
14086              * Fires before a tab is de-activated - can be used to do validation on a form.
14087              * @param {Roo.bootstrap.TabPanel} this
14088              * @return {Boolean} false if there is an error
14089             
14090          */
14091         'beforedeactivate': true
14092      });
14093     
14094     this.tabId = this.tabId || Roo.id();
14095   
14096 };
14097
14098 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
14099     
14100     active: false,
14101     html: false,
14102     tabId: false,
14103     navId : false,
14104     
14105     getAutoCreate : function(){
14106         var cfg = {
14107             tag: 'div',
14108             // item is needed for carousel - not sure if it has any effect otherwise
14109             cls: 'tab-pane item',
14110             html: this.html || ''
14111         };
14112         
14113         if(this.active){
14114             cfg.cls += ' active';
14115         }
14116         
14117         if(this.tabId){
14118             cfg.tabId = this.tabId;
14119         }
14120         
14121         
14122         return cfg;
14123     },
14124     
14125     initEvents:  function()
14126     {
14127         Roo.log('-------- init events on tab panel ---------');
14128         
14129         var p = this.parent();
14130         this.navId = this.navId || p.navId;
14131         
14132         if (typeof(this.navId) != 'undefined') {
14133             // not really needed.. but just in case.. parent should be a NavGroup.
14134             var tg = Roo.bootstrap.TabGroup.get(this.navId);
14135             Roo.log(['register', tg, this]);
14136             tg.register(this);
14137         }
14138     },
14139     
14140     
14141     onRender : function(ct, position)
14142     {
14143        // Roo.log("Call onRender: " + this.xtype);
14144         
14145         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
14146         
14147         
14148         
14149         
14150         
14151     },
14152     
14153     setActive: function(state)
14154     {
14155         Roo.log("panel - set active " + this.tabId + "=" + state);
14156         
14157         this.active = state;
14158         if (!state) {
14159             this.el.removeClass('active');
14160             
14161         } else  if (!this.el.hasClass('active')) {
14162             this.el.addClass('active');
14163         }
14164         this.fireEvent('changed', this, state);
14165     }
14166     
14167     
14168 });
14169  
14170
14171  
14172
14173  /*
14174  * - LGPL
14175  *
14176  * DateField
14177  * 
14178  */
14179
14180 /**
14181  * @class Roo.bootstrap.DateField
14182  * @extends Roo.bootstrap.Input
14183  * Bootstrap DateField class
14184  * @cfg {Number} weekStart default 0
14185  * @cfg {Number} weekStart default 0
14186  * @cfg {Number} viewMode default empty, (months|years)
14187  * @cfg {Number} minViewMode default empty, (months|years)
14188  * @cfg {Number} startDate default -Infinity
14189  * @cfg {Number} endDate default Infinity
14190  * @cfg {Boolean} todayHighlight default false
14191  * @cfg {Boolean} todayBtn default false
14192  * @cfg {Boolean} calendarWeeks default false
14193  * @cfg {Object} daysOfWeekDisabled default empty
14194  * 
14195  * @cfg {Boolean} keyboardNavigation default true
14196  * @cfg {String} language default en
14197  * 
14198  * @constructor
14199  * Create a new DateField
14200  * @param {Object} config The config object
14201  */
14202
14203 Roo.bootstrap.DateField = function(config){
14204     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
14205      this.addEvents({
14206             /**
14207              * @event show
14208              * Fires when this field show.
14209              * @param {Roo.bootstrap.DateField} this
14210              * @param {Mixed} date The date value
14211              */
14212             show : true,
14213             /**
14214              * @event show
14215              * Fires when this field hide.
14216              * @param {Roo.bootstrap.DateField} this
14217              * @param {Mixed} date The date value
14218              */
14219             hide : true,
14220             /**
14221              * @event select
14222              * Fires when select a date.
14223              * @param {Roo.bootstrap.DateField} this
14224              * @param {Mixed} date The date value
14225              */
14226             select : true
14227         });
14228 };
14229
14230 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
14231     
14232     /**
14233      * @cfg {String} format
14234      * The default date format string which can be overriden for localization support.  The format must be
14235      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
14236      */
14237     format : "m/d/y",
14238     /**
14239      * @cfg {String} altFormats
14240      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
14241      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
14242      */
14243     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
14244     
14245     weekStart : 0,
14246     
14247     viewMode : '',
14248     
14249     minViewMode : '',
14250     
14251     todayHighlight : false,
14252     
14253     todayBtn: false,
14254     
14255     language: 'en',
14256     
14257     keyboardNavigation: true,
14258     
14259     calendarWeeks: false,
14260     
14261     startDate: -Infinity,
14262     
14263     endDate: Infinity,
14264     
14265     daysOfWeekDisabled: [],
14266     
14267     _events: [],
14268     
14269     UTCDate: function()
14270     {
14271         return new Date(Date.UTC.apply(Date, arguments));
14272     },
14273     
14274     UTCToday: function()
14275     {
14276         var today = new Date();
14277         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
14278     },
14279     
14280     getDate: function() {
14281             var d = this.getUTCDate();
14282             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
14283     },
14284     
14285     getUTCDate: function() {
14286             return this.date;
14287     },
14288     
14289     setDate: function(d) {
14290             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
14291     },
14292     
14293     setUTCDate: function(d) {
14294             this.date = d;
14295             this.setValue(this.formatDate(this.date));
14296     },
14297         
14298     onRender: function(ct, position)
14299     {
14300         
14301         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
14302         
14303         this.language = this.language || 'en';
14304         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
14305         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
14306         
14307         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
14308         this.format = this.format || 'm/d/y';
14309         this.isInline = false;
14310         this.isInput = true;
14311         this.component = this.el.select('.add-on', true).first() || false;
14312         this.component = (this.component && this.component.length === 0) ? false : this.component;
14313         this.hasInput = this.component && this.inputEL().length;
14314         
14315         if (typeof(this.minViewMode === 'string')) {
14316             switch (this.minViewMode) {
14317                 case 'months':
14318                     this.minViewMode = 1;
14319                     break;
14320                 case 'years':
14321                     this.minViewMode = 2;
14322                     break;
14323                 default:
14324                     this.minViewMode = 0;
14325                     break;
14326             }
14327         }
14328         
14329         if (typeof(this.viewMode === 'string')) {
14330             switch (this.viewMode) {
14331                 case 'months':
14332                     this.viewMode = 1;
14333                     break;
14334                 case 'years':
14335                     this.viewMode = 2;
14336                     break;
14337                 default:
14338                     this.viewMode = 0;
14339                     break;
14340             }
14341         }
14342                 
14343         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
14344         
14345 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
14346         
14347         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14348         
14349         this.picker().on('mousedown', this.onMousedown, this);
14350         this.picker().on('click', this.onClick, this);
14351         
14352         this.picker().addClass('datepicker-dropdown');
14353         
14354         this.startViewMode = this.viewMode;
14355         
14356         
14357         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
14358             if(!this.calendarWeeks){
14359                 v.remove();
14360                 return;
14361             };
14362             
14363             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
14364             v.attr('colspan', function(i, val){
14365                 return parseInt(val) + 1;
14366             });
14367         })
14368                         
14369         
14370         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
14371         
14372         this.setStartDate(this.startDate);
14373         this.setEndDate(this.endDate);
14374         
14375         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
14376         
14377         this.fillDow();
14378         this.fillMonths();
14379         this.update();
14380         this.showMode();
14381         
14382         if(this.isInline) {
14383             this.show();
14384         }
14385     },
14386     
14387     picker : function()
14388     {
14389         return this.pickerEl;
14390 //        return this.el.select('.datepicker', true).first();
14391     },
14392     
14393     fillDow: function()
14394     {
14395         var dowCnt = this.weekStart;
14396         
14397         var dow = {
14398             tag: 'tr',
14399             cn: [
14400                 
14401             ]
14402         };
14403         
14404         if(this.calendarWeeks){
14405             dow.cn.push({
14406                 tag: 'th',
14407                 cls: 'cw',
14408                 html: '&nbsp;'
14409             })
14410         }
14411         
14412         while (dowCnt < this.weekStart + 7) {
14413             dow.cn.push({
14414                 tag: 'th',
14415                 cls: 'dow',
14416                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
14417             });
14418         }
14419         
14420         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
14421     },
14422     
14423     fillMonths: function()
14424     {    
14425         var i = 0
14426         var months = this.picker().select('>.datepicker-months td', true).first();
14427         
14428         months.dom.innerHTML = '';
14429         
14430         while (i < 12) {
14431             var month = {
14432                 tag: 'span',
14433                 cls: 'month',
14434                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
14435             }
14436             
14437             months.createChild(month);
14438         }
14439         
14440     },
14441     
14442     update: function()
14443     {
14444         
14445         this.date = (typeof(this.date) === 'undefined') ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
14446         
14447         if (this.date < this.startDate) {
14448             this.viewDate = new Date(this.startDate);
14449         } else if (this.date > this.endDate) {
14450             this.viewDate = new Date(this.endDate);
14451         } else {
14452             this.viewDate = new Date(this.date);
14453         }
14454         
14455         this.fill();
14456     },
14457     
14458     fill: function() 
14459     {
14460         var d = new Date(this.viewDate),
14461                 year = d.getUTCFullYear(),
14462                 month = d.getUTCMonth(),
14463                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
14464                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
14465                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
14466                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
14467                 currentDate = this.date && this.date.valueOf(),
14468                 today = this.UTCToday();
14469         
14470         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
14471         
14472 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
14473         
14474 //        this.picker.select('>tfoot th.today').
14475 //                                              .text(dates[this.language].today)
14476 //                                              .toggle(this.todayBtn !== false);
14477     
14478         this.updateNavArrows();
14479         this.fillMonths();
14480                                                 
14481         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
14482         
14483         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
14484          
14485         prevMonth.setUTCDate(day);
14486         
14487         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
14488         
14489         var nextMonth = new Date(prevMonth);
14490         
14491         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
14492         
14493         nextMonth = nextMonth.valueOf();
14494         
14495         var fillMonths = false;
14496         
14497         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
14498         
14499         while(prevMonth.valueOf() < nextMonth) {
14500             var clsName = '';
14501             
14502             if (prevMonth.getUTCDay() === this.weekStart) {
14503                 if(fillMonths){
14504                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
14505                 }
14506                     
14507                 fillMonths = {
14508                     tag: 'tr',
14509                     cn: []
14510                 };
14511                 
14512                 if(this.calendarWeeks){
14513                     // ISO 8601: First week contains first thursday.
14514                     // ISO also states week starts on Monday, but we can be more abstract here.
14515                     var
14516                     // Start of current week: based on weekstart/current date
14517                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
14518                     // Thursday of this week
14519                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
14520                     // First Thursday of year, year from thursday
14521                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
14522                     // Calendar week: ms between thursdays, div ms per day, div 7 days
14523                     calWeek =  (th - yth) / 864e5 / 7 + 1;
14524                     
14525                     fillMonths.cn.push({
14526                         tag: 'td',
14527                         cls: 'cw',
14528                         html: calWeek
14529                     });
14530                 }
14531             }
14532             
14533             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
14534                 clsName += ' old';
14535             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
14536                 clsName += ' new';
14537             }
14538             if (this.todayHighlight &&
14539                 prevMonth.getUTCFullYear() == today.getFullYear() &&
14540                 prevMonth.getUTCMonth() == today.getMonth() &&
14541                 prevMonth.getUTCDate() == today.getDate()) {
14542                 clsName += ' today';
14543             }
14544             
14545             if (currentDate && prevMonth.valueOf() === currentDate) {
14546                 clsName += ' active';
14547             }
14548             
14549             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
14550                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
14551                     clsName += ' disabled';
14552             }
14553             
14554             fillMonths.cn.push({
14555                 tag: 'td',
14556                 cls: 'day ' + clsName,
14557                 html: prevMonth.getDate()
14558             })
14559             
14560             prevMonth.setDate(prevMonth.getDate()+1);
14561         }
14562           
14563         var currentYear = this.date && this.date.getUTCFullYear();
14564         var currentMonth = this.date && this.date.getUTCMonth();
14565         
14566         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
14567         
14568         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
14569             v.removeClass('active');
14570             
14571             if(currentYear === year && k === currentMonth){
14572                 v.addClass('active');
14573             }
14574             
14575             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
14576                 v.addClass('disabled');
14577             }
14578             
14579         });
14580         
14581         
14582         year = parseInt(year/10, 10) * 10;
14583         
14584         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
14585         
14586         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
14587         
14588         year -= 1;
14589         for (var i = -1; i < 11; i++) {
14590             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
14591                 tag: 'span',
14592                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
14593                 html: year
14594             })
14595             
14596             year += 1;
14597         }
14598     },
14599     
14600     showMode: function(dir) 
14601     {
14602         if (dir) {
14603             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
14604         }
14605         Roo.each(this.picker().select('>div',true).elements, function(v){
14606             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14607             v.hide();
14608         });
14609         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
14610     },
14611     
14612     place: function()
14613     {
14614         if(this.isInline) return;
14615         
14616         this.picker().removeClass(['bottom', 'top']);
14617         
14618         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
14619             /*
14620              * place to the top of element!
14621              *
14622              */
14623             
14624             this.picker().addClass('top');
14625             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
14626             
14627             return;
14628         }
14629         
14630         this.picker().addClass('bottom');
14631         
14632         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
14633     },
14634     
14635     parseDate : function(value)
14636     {
14637         if(!value || value instanceof Date){
14638             return value;
14639         }
14640         var v = Date.parseDate(value, this.format);
14641         if (!v && this.useIso) {
14642             v = Date.parseDate(value, 'Y-m-d');
14643         }
14644         if(!v && this.altFormats){
14645             if(!this.altFormatsArray){
14646                 this.altFormatsArray = this.altFormats.split("|");
14647             }
14648             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
14649                 v = Date.parseDate(value, this.altFormatsArray[i]);
14650             }
14651         }
14652         return v;
14653     },
14654     
14655     formatDate : function(date, fmt)
14656     {
14657         return (!date || !(date instanceof Date)) ?
14658         date : date.dateFormat(fmt || this.format);
14659     },
14660     
14661     onFocus : function()
14662     {
14663         Roo.bootstrap.DateField.superclass.onFocus.call(this);
14664         this.show();
14665     },
14666     
14667     onBlur : function()
14668     {
14669         Roo.bootstrap.DateField.superclass.onBlur.call(this);
14670         
14671         var d = this.inputEl().getValue();
14672         
14673         if(d && d.length){
14674             this.setValue(d);
14675         }
14676                 
14677         this.hide();
14678     },
14679     
14680     show : function()
14681     {
14682         this.picker().show();
14683         this.update();
14684         this.place();
14685         
14686         this.fireEvent('show', this, this.date);
14687     },
14688     
14689     hide : function()
14690     {
14691         if(this.isInline) return;
14692         this.picker().hide();
14693         this.viewMode = this.startViewMode;
14694         this.showMode();
14695         
14696         this.fireEvent('hide', this, this.date);
14697         
14698     },
14699     
14700     onMousedown: function(e)
14701     {
14702         e.stopPropagation();
14703         e.preventDefault();
14704     },
14705     
14706     keyup: function(e)
14707     {
14708         Roo.bootstrap.DateField.superclass.keyup.call(this);
14709         this.update();
14710     },
14711
14712     setValue: function(v)
14713     {
14714         var d = new Date(v);
14715         
14716         if(isNaN(d.getTime())){
14717             return;
14718         }
14719         
14720         v = this.formatDate(d);
14721         
14722         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
14723         
14724         this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
14725
14726         this.update();
14727
14728         this.fireEvent('select', this, this.date);
14729         
14730     },
14731     
14732     getValue: function()
14733     {
14734         return this.formatDate(this.date);
14735     },
14736     
14737     fireKey: function(e)
14738     {
14739         if (!this.picker().isVisible()){
14740             if (e.keyCode == 27) // allow escape to hide and re-show picker
14741                 this.show();
14742             return;
14743         }
14744         
14745         var dateChanged = false,
14746         dir, day, month,
14747         newDate, newViewDate;
14748         
14749         switch(e.keyCode){
14750             case 27: // escape
14751                 this.hide();
14752                 e.preventDefault();
14753                 break;
14754             case 37: // left
14755             case 39: // right
14756                 if (!this.keyboardNavigation) break;
14757                 dir = e.keyCode == 37 ? -1 : 1;
14758                 
14759                 if (e.ctrlKey){
14760                     newDate = this.moveYear(this.date, dir);
14761                     newViewDate = this.moveYear(this.viewDate, dir);
14762                 } else if (e.shiftKey){
14763                     newDate = this.moveMonth(this.date, dir);
14764                     newViewDate = this.moveMonth(this.viewDate, dir);
14765                 } else {
14766                     newDate = new Date(this.date);
14767                     newDate.setUTCDate(this.date.getUTCDate() + dir);
14768                     newViewDate = new Date(this.viewDate);
14769                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
14770                 }
14771                 if (this.dateWithinRange(newDate)){
14772                     this.date = newDate;
14773                     this.viewDate = newViewDate;
14774                     this.setValue(this.formatDate(this.date));
14775 //                    this.update();
14776                     e.preventDefault();
14777                     dateChanged = true;
14778                 }
14779                 break;
14780             case 38: // up
14781             case 40: // down
14782                 if (!this.keyboardNavigation) break;
14783                 dir = e.keyCode == 38 ? -1 : 1;
14784                 if (e.ctrlKey){
14785                     newDate = this.moveYear(this.date, dir);
14786                     newViewDate = this.moveYear(this.viewDate, dir);
14787                 } else if (e.shiftKey){
14788                     newDate = this.moveMonth(this.date, dir);
14789                     newViewDate = this.moveMonth(this.viewDate, dir);
14790                 } else {
14791                     newDate = new Date(this.date);
14792                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
14793                     newViewDate = new Date(this.viewDate);
14794                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
14795                 }
14796                 if (this.dateWithinRange(newDate)){
14797                     this.date = newDate;
14798                     this.viewDate = newViewDate;
14799                     this.setValue(this.formatDate(this.date));
14800 //                    this.update();
14801                     e.preventDefault();
14802                     dateChanged = true;
14803                 }
14804                 break;
14805             case 13: // enter
14806                 this.setValue(this.formatDate(this.date));
14807                 this.hide();
14808                 e.preventDefault();
14809                 break;
14810             case 9: // tab
14811                 this.setValue(this.formatDate(this.date));
14812                 this.hide();
14813                 break;
14814             case 16: // shift
14815             case 17: // ctrl
14816             case 18: // alt
14817                 break;
14818             default :
14819                 this.hide();
14820                 
14821         }
14822     },
14823     
14824     
14825     onClick: function(e) 
14826     {
14827         e.stopPropagation();
14828         e.preventDefault();
14829         
14830         var target = e.getTarget();
14831         
14832         if(target.nodeName.toLowerCase() === 'i'){
14833             target = Roo.get(target).dom.parentNode;
14834         }
14835         
14836         var nodeName = target.nodeName;
14837         var className = target.className;
14838         var html = target.innerHTML;
14839         
14840         switch(nodeName.toLowerCase()) {
14841             case 'th':
14842                 switch(className) {
14843                     case 'switch':
14844                         this.showMode(1);
14845                         break;
14846                     case 'prev':
14847                     case 'next':
14848                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
14849                         switch(this.viewMode){
14850                                 case 0:
14851                                         this.viewDate = this.moveMonth(this.viewDate, dir);
14852                                         break;
14853                                 case 1:
14854                                 case 2:
14855                                         this.viewDate = this.moveYear(this.viewDate, dir);
14856                                         break;
14857                         }
14858                         this.fill();
14859                         break;
14860                     case 'today':
14861                         var date = new Date();
14862                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
14863 //                        this.fill()
14864                         this.setValue(this.formatDate(this.date));
14865                         
14866                         this.hide();
14867                         break;
14868                 }
14869                 break;
14870             case 'span':
14871                 if (className.indexOf('disabled') === -1) {
14872                     this.viewDate.setUTCDate(1);
14873                     if (className.indexOf('month') !== -1) {
14874                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
14875                     } else {
14876                         var year = parseInt(html, 10) || 0;
14877                         this.viewDate.setUTCFullYear(year);
14878                         
14879                     }
14880                     this.showMode(-1);
14881                     this.fill();
14882                 }
14883                 break;
14884                 
14885             case 'td':
14886                 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
14887                     var day = parseInt(html, 10) || 1;
14888                     var year = this.viewDate.getUTCFullYear(),
14889                         month = this.viewDate.getUTCMonth();
14890
14891                     if (className.indexOf('old') !== -1) {
14892                         if(month === 0 ){
14893                             month = 11;
14894                             year -= 1;
14895                         }else{
14896                             month -= 1;
14897                         }
14898                     } else if (className.indexOf('new') !== -1) {
14899                         if (month == 11) {
14900                             month = 0;
14901                             year += 1;
14902                         } else {
14903                             month += 1;
14904                         }
14905                     }
14906                     this.date = this.UTCDate(year, month, day,0,0,0,0);
14907                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
14908 //                    this.fill();
14909                     this.setValue(this.formatDate(this.date));
14910                     this.hide();
14911                 }
14912                 break;
14913         }
14914     },
14915     
14916     setStartDate: function(startDate)
14917     {
14918         this.startDate = startDate || -Infinity;
14919         if (this.startDate !== -Infinity) {
14920             this.startDate = this.parseDate(this.startDate);
14921         }
14922         this.update();
14923         this.updateNavArrows();
14924     },
14925
14926     setEndDate: function(endDate)
14927     {
14928         this.endDate = endDate || Infinity;
14929         if (this.endDate !== Infinity) {
14930             this.endDate = this.parseDate(this.endDate);
14931         }
14932         this.update();
14933         this.updateNavArrows();
14934     },
14935     
14936     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
14937     {
14938         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
14939         if (typeof(this.daysOfWeekDisabled) !== 'object') {
14940             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
14941         }
14942         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
14943             return parseInt(d, 10);
14944         });
14945         this.update();
14946         this.updateNavArrows();
14947     },
14948     
14949     updateNavArrows: function() 
14950     {
14951         var d = new Date(this.viewDate),
14952         year = d.getUTCFullYear(),
14953         month = d.getUTCMonth();
14954         
14955         Roo.each(this.picker().select('.prev', true).elements, function(v){
14956             v.show();
14957             switch (this.viewMode) {
14958                 case 0:
14959
14960                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
14961                         v.hide();
14962                     }
14963                     break;
14964                 case 1:
14965                 case 2:
14966                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
14967                         v.hide();
14968                     }
14969                     break;
14970             }
14971         });
14972         
14973         Roo.each(this.picker().select('.next', true).elements, function(v){
14974             v.show();
14975             switch (this.viewMode) {
14976                 case 0:
14977
14978                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
14979                         v.hide();
14980                     }
14981                     break;
14982                 case 1:
14983                 case 2:
14984                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
14985                         v.hide();
14986                     }
14987                     break;
14988             }
14989         })
14990     },
14991     
14992     moveMonth: function(date, dir)
14993     {
14994         if (!dir) return date;
14995         var new_date = new Date(date.valueOf()),
14996         day = new_date.getUTCDate(),
14997         month = new_date.getUTCMonth(),
14998         mag = Math.abs(dir),
14999         new_month, test;
15000         dir = dir > 0 ? 1 : -1;
15001         if (mag == 1){
15002             test = dir == -1
15003             // If going back one month, make sure month is not current month
15004             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
15005             ? function(){
15006                 return new_date.getUTCMonth() == month;
15007             }
15008             // If going forward one month, make sure month is as expected
15009             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
15010             : function(){
15011                 return new_date.getUTCMonth() != new_month;
15012             };
15013             new_month = month + dir;
15014             new_date.setUTCMonth(new_month);
15015             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
15016             if (new_month < 0 || new_month > 11)
15017                 new_month = (new_month + 12) % 12;
15018         } else {
15019             // For magnitudes >1, move one month at a time...
15020             for (var i=0; i<mag; i++)
15021                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
15022                 new_date = this.moveMonth(new_date, dir);
15023             // ...then reset the day, keeping it in the new month
15024             new_month = new_date.getUTCMonth();
15025             new_date.setUTCDate(day);
15026             test = function(){
15027                 return new_month != new_date.getUTCMonth();
15028             };
15029         }
15030         // Common date-resetting loop -- if date is beyond end of month, make it
15031         // end of month
15032         while (test()){
15033             new_date.setUTCDate(--day);
15034             new_date.setUTCMonth(new_month);
15035         }
15036         return new_date;
15037     },
15038
15039     moveYear: function(date, dir)
15040     {
15041         return this.moveMonth(date, dir*12);
15042     },
15043
15044     dateWithinRange: function(date)
15045     {
15046         return date >= this.startDate && date <= this.endDate;
15047     },
15048
15049     
15050     remove: function() 
15051     {
15052         this.picker().remove();
15053     }
15054    
15055 });
15056
15057 Roo.apply(Roo.bootstrap.DateField,  {
15058     
15059     head : {
15060         tag: 'thead',
15061         cn: [
15062         {
15063             tag: 'tr',
15064             cn: [
15065             {
15066                 tag: 'th',
15067                 cls: 'prev',
15068                 html: '<i class="fa fa-arrow-left"/>'
15069             },
15070             {
15071                 tag: 'th',
15072                 cls: 'switch',
15073                 colspan: '5'
15074             },
15075             {
15076                 tag: 'th',
15077                 cls: 'next',
15078                 html: '<i class="fa fa-arrow-right"/>'
15079             }
15080
15081             ]
15082         }
15083         ]
15084     },
15085     
15086     content : {
15087         tag: 'tbody',
15088         cn: [
15089         {
15090             tag: 'tr',
15091             cn: [
15092             {
15093                 tag: 'td',
15094                 colspan: '7'
15095             }
15096             ]
15097         }
15098         ]
15099     },
15100     
15101     footer : {
15102         tag: 'tfoot',
15103         cn: [
15104         {
15105             tag: 'tr',
15106             cn: [
15107             {
15108                 tag: 'th',
15109                 colspan: '7',
15110                 cls: 'today'
15111             }
15112                     
15113             ]
15114         }
15115         ]
15116     },
15117     
15118     dates:{
15119         en: {
15120             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
15121             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
15122             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
15123             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
15124             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
15125             today: "Today"
15126         }
15127     },
15128     
15129     modes: [
15130     {
15131         clsName: 'days',
15132         navFnc: 'Month',
15133         navStep: 1
15134     },
15135     {
15136         clsName: 'months',
15137         navFnc: 'FullYear',
15138         navStep: 1
15139     },
15140     {
15141         clsName: 'years',
15142         navFnc: 'FullYear',
15143         navStep: 10
15144     }]
15145 });
15146
15147 Roo.apply(Roo.bootstrap.DateField,  {
15148   
15149     template : {
15150         tag: 'div',
15151         cls: 'datepicker dropdown-menu',
15152         cn: [
15153         {
15154             tag: 'div',
15155             cls: 'datepicker-days',
15156             cn: [
15157             {
15158                 tag: 'table',
15159                 cls: 'table-condensed',
15160                 cn:[
15161                 Roo.bootstrap.DateField.head,
15162                 {
15163                     tag: 'tbody'
15164                 },
15165                 Roo.bootstrap.DateField.footer
15166                 ]
15167             }
15168             ]
15169         },
15170         {
15171             tag: 'div',
15172             cls: 'datepicker-months',
15173             cn: [
15174             {
15175                 tag: 'table',
15176                 cls: 'table-condensed',
15177                 cn:[
15178                 Roo.bootstrap.DateField.head,
15179                 Roo.bootstrap.DateField.content,
15180                 Roo.bootstrap.DateField.footer
15181                 ]
15182             }
15183             ]
15184         },
15185         {
15186             tag: 'div',
15187             cls: 'datepicker-years',
15188             cn: [
15189             {
15190                 tag: 'table',
15191                 cls: 'table-condensed',
15192                 cn:[
15193                 Roo.bootstrap.DateField.head,
15194                 Roo.bootstrap.DateField.content,
15195                 Roo.bootstrap.DateField.footer
15196                 ]
15197             }
15198             ]
15199         }
15200         ]
15201     }
15202 });
15203
15204  
15205
15206  /*
15207  * - LGPL
15208  *
15209  * TimeField
15210  * 
15211  */
15212
15213 /**
15214  * @class Roo.bootstrap.TimeField
15215  * @extends Roo.bootstrap.Input
15216  * Bootstrap DateField class
15217  * 
15218  * 
15219  * @constructor
15220  * Create a new TimeField
15221  * @param {Object} config The config object
15222  */
15223
15224 Roo.bootstrap.TimeField = function(config){
15225     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
15226     this.addEvents({
15227             /**
15228              * @event show
15229              * Fires when this field show.
15230              * @param {Roo.bootstrap.DateField} this
15231              * @param {Mixed} date The date value
15232              */
15233             show : true,
15234             /**
15235              * @event show
15236              * Fires when this field hide.
15237              * @param {Roo.bootstrap.DateField} this
15238              * @param {Mixed} date The date value
15239              */
15240             hide : true,
15241             /**
15242              * @event select
15243              * Fires when select a date.
15244              * @param {Roo.bootstrap.DateField} this
15245              * @param {Mixed} date The date value
15246              */
15247             select : true
15248         });
15249 };
15250
15251 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
15252     
15253     /**
15254      * @cfg {String} format
15255      * The default time format string which can be overriden for localization support.  The format must be
15256      * valid according to {@link Date#parseDate} (defaults to 'H:i').
15257      */
15258     format : "H:i",
15259        
15260     onRender: function(ct, position)
15261     {
15262         
15263         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
15264                 
15265         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
15266         
15267         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15268         
15269         this.pop = this.picker().select('>.datepicker-time',true).first();
15270         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block' 
15271         
15272         this.picker().on('mousedown', this.onMousedown, this);
15273         this.picker().on('click', this.onClick, this);
15274         
15275         this.picker().addClass('datepicker-dropdown');
15276     
15277         this.fillTime();
15278         this.update();
15279             
15280         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
15281         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
15282         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
15283         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
15284         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
15285         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
15286
15287     },
15288     
15289     fireKey: function(e){
15290         if (!this.picker().isVisible()){
15291             if (e.keyCode == 27) // allow escape to hide and re-show picker
15292                 this.show();
15293             return;
15294         }
15295
15296         e.preventDefault();
15297         
15298         switch(e.keyCode){
15299             case 27: // escape
15300                 this.hide();
15301                 break;
15302             case 37: // left
15303             case 39: // right
15304                 this.onTogglePeriod();
15305                 break;
15306             case 38: // up
15307                 this.onIncrementMinutes();
15308                 break;
15309             case 40: // down
15310                 this.onDecrementMinutes();
15311                 break;
15312             case 13: // enter
15313             case 9: // tab
15314                 this.setTime();
15315                 break;
15316         }
15317     },
15318     
15319     onClick: function(e) {
15320         e.stopPropagation();
15321         e.preventDefault();
15322     },
15323     
15324     picker : function()
15325     {
15326         return this.el.select('.datepicker', true).first();
15327     },
15328     
15329     fillTime: function()
15330     {    
15331         var time = this.pop.select('tbody', true).first();
15332         
15333         time.dom.innerHTML = '';
15334         
15335         time.createChild({
15336             tag: 'tr',
15337             cn: [
15338                 {
15339                     tag: 'td',
15340                     cn: [
15341                         {
15342                             tag: 'a',
15343                             href: '#',
15344                             cls: 'btn',
15345                             cn: [
15346                                 {
15347                                     tag: 'span',
15348                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
15349                                 }
15350                             ]
15351                         } 
15352                     ]
15353                 },
15354                 {
15355                     tag: 'td',
15356                     cls: 'separator'
15357                 },
15358                 {
15359                     tag: 'td',
15360                     cn: [
15361                         {
15362                             tag: 'a',
15363                             href: '#',
15364                             cls: 'btn',
15365                             cn: [
15366                                 {
15367                                     tag: 'span',
15368                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
15369                                 }
15370                             ]
15371                         }
15372                     ]
15373                 },
15374                 {
15375                     tag: 'td',
15376                     cls: 'separator'
15377                 }
15378             ]
15379         });
15380         
15381         time.createChild({
15382             tag: 'tr',
15383             cn: [
15384                 {
15385                     tag: 'td',
15386                     cn: [
15387                         {
15388                             tag: 'span',
15389                             cls: 'timepicker-hour',
15390                             html: '00'
15391                         }  
15392                     ]
15393                 },
15394                 {
15395                     tag: 'td',
15396                     cls: 'separator',
15397                     html: ':'
15398                 },
15399                 {
15400                     tag: 'td',
15401                     cn: [
15402                         {
15403                             tag: 'span',
15404                             cls: 'timepicker-minute',
15405                             html: '00'
15406                         }  
15407                     ]
15408                 },
15409                 {
15410                     tag: 'td',
15411                     cls: 'separator'
15412                 },
15413                 {
15414                     tag: 'td',
15415                     cn: [
15416                         {
15417                             tag: 'button',
15418                             type: 'button',
15419                             cls: 'btn btn-primary period',
15420                             html: 'AM'
15421                             
15422                         }
15423                     ]
15424                 }
15425             ]
15426         });
15427         
15428         time.createChild({
15429             tag: 'tr',
15430             cn: [
15431                 {
15432                     tag: 'td',
15433                     cn: [
15434                         {
15435                             tag: 'a',
15436                             href: '#',
15437                             cls: 'btn',
15438                             cn: [
15439                                 {
15440                                     tag: 'span',
15441                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
15442                                 }
15443                             ]
15444                         }
15445                     ]
15446                 },
15447                 {
15448                     tag: 'td',
15449                     cls: 'separator'
15450                 },
15451                 {
15452                     tag: 'td',
15453                     cn: [
15454                         {
15455                             tag: 'a',
15456                             href: '#',
15457                             cls: 'btn',
15458                             cn: [
15459                                 {
15460                                     tag: 'span',
15461                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
15462                                 }
15463                             ]
15464                         }
15465                     ]
15466                 },
15467                 {
15468                     tag: 'td',
15469                     cls: 'separator'
15470                 }
15471             ]
15472         });
15473         
15474     },
15475     
15476     update: function()
15477     {
15478         
15479         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
15480         
15481         this.fill();
15482     },
15483     
15484     fill: function() 
15485     {
15486         var hours = this.time.getHours();
15487         var minutes = this.time.getMinutes();
15488         var period = 'AM';
15489         
15490         if(hours > 11){
15491             period = 'PM';
15492         }
15493         
15494         if(hours == 0){
15495             hours = 12;
15496         }
15497         
15498         
15499         if(hours > 12){
15500             hours = hours - 12;
15501         }
15502         
15503         if(hours < 10){
15504             hours = '0' + hours;
15505         }
15506         
15507         if(minutes < 10){
15508             minutes = '0' + minutes;
15509         }
15510         
15511         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
15512         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
15513         this.pop.select('button', true).first().dom.innerHTML = period;
15514         
15515     },
15516     
15517     place: function()
15518     {   
15519         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
15520         
15521         var cls = ['bottom'];
15522         
15523         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
15524             cls.pop();
15525             cls.push('top');
15526         }
15527         
15528         cls.push('right');
15529         
15530         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
15531             cls.pop();
15532             cls.push('left');
15533         }
15534         
15535         this.picker().addClass(cls.join('-'));
15536         
15537         var _this = this;
15538         
15539         Roo.each(cls, function(c){
15540             if(c == 'bottom'){
15541                 _this.picker().setTop(_this.inputEl().getHeight());
15542                 return;
15543             }
15544             if(c == 'top'){
15545                 _this.picker().setTop(0 - _this.picker().getHeight());
15546                 return;
15547             }
15548             
15549             if(c == 'left'){
15550                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
15551                 return;
15552             }
15553             if(c == 'right'){
15554                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
15555                 return;
15556             }
15557         });
15558         
15559     },
15560   
15561     onFocus : function()
15562     {
15563         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
15564         this.show();
15565     },
15566     
15567     onBlur : function()
15568     {
15569         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
15570         this.hide();
15571     },
15572     
15573     show : function()
15574     {
15575         this.picker().show();
15576         this.pop.show();
15577         this.update();
15578         this.place();
15579         
15580         this.fireEvent('show', this, this.date);
15581     },
15582     
15583     hide : function()
15584     {
15585         this.picker().hide();
15586         this.pop.hide();
15587         
15588         this.fireEvent('hide', this, this.date);
15589     },
15590     
15591     setTime : function()
15592     {
15593         this.hide();
15594         this.setValue(this.time.format(this.format));
15595         
15596         this.fireEvent('select', this, this.date);
15597         
15598         
15599     },
15600     
15601     onMousedown: function(e){
15602         e.stopPropagation();
15603         e.preventDefault();
15604     },
15605     
15606     onIncrementHours: function()
15607     {
15608         Roo.log('onIncrementHours');
15609         this.time = this.time.add(Date.HOUR, 1);
15610         this.update();
15611         
15612     },
15613     
15614     onDecrementHours: function()
15615     {
15616         Roo.log('onDecrementHours');
15617         this.time = this.time.add(Date.HOUR, -1);
15618         this.update();
15619     },
15620     
15621     onIncrementMinutes: function()
15622     {
15623         Roo.log('onIncrementMinutes');
15624         this.time = this.time.add(Date.MINUTE, 1);
15625         this.update();
15626     },
15627     
15628     onDecrementMinutes: function()
15629     {
15630         Roo.log('onDecrementMinutes');
15631         this.time = this.time.add(Date.MINUTE, -1);
15632         this.update();
15633     },
15634     
15635     onTogglePeriod: function()
15636     {
15637         Roo.log('onTogglePeriod');
15638         this.time = this.time.add(Date.HOUR, 12);
15639         this.update();
15640     }
15641     
15642    
15643 });
15644
15645 Roo.apply(Roo.bootstrap.TimeField,  {
15646     
15647     content : {
15648         tag: 'tbody',
15649         cn: [
15650             {
15651                 tag: 'tr',
15652                 cn: [
15653                 {
15654                     tag: 'td',
15655                     colspan: '7'
15656                 }
15657                 ]
15658             }
15659         ]
15660     },
15661     
15662     footer : {
15663         tag: 'tfoot',
15664         cn: [
15665             {
15666                 tag: 'tr',
15667                 cn: [
15668                 {
15669                     tag: 'th',
15670                     colspan: '7',
15671                     cls: '',
15672                     cn: [
15673                         {
15674                             tag: 'button',
15675                             cls: 'btn btn-info ok',
15676                             html: 'OK'
15677                         }
15678                     ]
15679                 }
15680
15681                 ]
15682             }
15683         ]
15684     }
15685 });
15686
15687 Roo.apply(Roo.bootstrap.TimeField,  {
15688   
15689     template : {
15690         tag: 'div',
15691         cls: 'datepicker dropdown-menu',
15692         cn: [
15693             {
15694                 tag: 'div',
15695                 cls: 'datepicker-time',
15696                 cn: [
15697                 {
15698                     tag: 'table',
15699                     cls: 'table-condensed',
15700                     cn:[
15701                     Roo.bootstrap.TimeField.content,
15702                     Roo.bootstrap.TimeField.footer
15703                     ]
15704                 }
15705                 ]
15706             }
15707         ]
15708     }
15709 });
15710
15711  
15712
15713  /*
15714  * - LGPL
15715  *
15716  * CheckBox
15717  * 
15718  */
15719
15720 /**
15721  * @class Roo.bootstrap.CheckBox
15722  * @extends Roo.bootstrap.Input
15723  * Bootstrap CheckBox class
15724  * 
15725  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
15726  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
15727  * @cfg {String} boxLabel The text that appears beside the checkbox
15728  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
15729  * @cfg {Boolean} checked initnal the element
15730  * 
15731  * 
15732  * @constructor
15733  * Create a new CheckBox
15734  * @param {Object} config The config object
15735  */
15736
15737 Roo.bootstrap.CheckBox = function(config){
15738     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
15739    
15740         this.addEvents({
15741             /**
15742             * @event check
15743             * Fires when the element is checked or unchecked.
15744             * @param {Roo.bootstrap.CheckBox} this This input
15745             * @param {Boolean} checked The new checked value
15746             */
15747            check : true
15748         });
15749 };
15750
15751 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
15752     
15753     inputType: 'checkbox',
15754     inputValue: 1,
15755     valueOff: 0,
15756     boxLabel: false,
15757     checked: false,
15758     weight : false,
15759     
15760     getAutoCreate : function()
15761     {
15762         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
15763         
15764         var id = Roo.id();
15765         
15766         var cfg = {};
15767         
15768         cfg.cls = 'form-group checkbox' //input-group
15769         
15770         
15771         
15772         
15773         var input =  {
15774             tag: 'input',
15775             id : id,
15776             type : this.inputType,
15777             value : (!this.checked) ? this.valueOff : this.inputValue,
15778             cls : 'roo-checkbox', //'form-box',
15779             placeholder : this.placeholder || ''
15780             
15781         };
15782         
15783         if (this.weight) { // Validity check?
15784             cfg.cls += " checkbox-" + this.weight;
15785         }
15786         
15787         if (this.disabled) {
15788             input.disabled=true;
15789         }
15790         
15791         if(this.checked){
15792             input.checked = this.checked;
15793         }
15794         
15795         if (this.name) {
15796             input.name = this.name;
15797         }
15798         
15799         if (this.size) {
15800             input.cls += ' input-' + this.size;
15801         }
15802         
15803         var settings=this;
15804         ['xs','sm','md','lg'].map(function(size){
15805             if (settings[size]) {
15806                 cfg.cls += ' col-' + size + '-' + settings[size];
15807             }
15808         });
15809         
15810        
15811         
15812         var inputblock = input;
15813         
15814         
15815         
15816         
15817         if (this.before || this.after) {
15818             
15819             inputblock = {
15820                 cls : 'input-group',
15821                 cn :  [] 
15822             };
15823             if (this.before) {
15824                 inputblock.cn.push({
15825                     tag :'span',
15826                     cls : 'input-group-addon',
15827                     html : this.before
15828                 });
15829             }
15830             inputblock.cn.push(input);
15831             if (this.after) {
15832                 inputblock.cn.push({
15833                     tag :'span',
15834                     cls : 'input-group-addon',
15835                     html : this.after
15836                 });
15837             }
15838             
15839         };
15840         
15841         if (align ==='left' && this.fieldLabel.length) {
15842                 Roo.log("left and has label");
15843                 cfg.cn = [
15844                     
15845                     {
15846                         tag: 'label',
15847                         'for' :  id,
15848                         cls : 'control-label col-md-' + this.labelWidth,
15849                         html : this.fieldLabel
15850                         
15851                     },
15852                     {
15853                         cls : "col-md-" + (12 - this.labelWidth), 
15854                         cn: [
15855                             inputblock
15856                         ]
15857                     }
15858                     
15859                 ];
15860         } else if ( this.fieldLabel.length) {
15861                 Roo.log(" label");
15862                 cfg.cn = [
15863                    
15864                     {
15865                         tag: this.boxLabel ? 'span' : 'label',
15866                         'for': id,
15867                         cls: 'control-label box-input-label',
15868                         //cls : 'input-group-addon',
15869                         html : this.fieldLabel
15870                         
15871                     },
15872                     
15873                     inputblock
15874                     
15875                 ];
15876
15877         } else {
15878             
15879                 Roo.log(" no label && no align");
15880                 cfg.cn = [  inputblock ] ;
15881                 
15882                 
15883         };
15884          if(this.boxLabel){
15885             cfg.cn.push( {
15886                 tag: 'label',
15887                 'for': id,
15888                 cls: 'box-label',
15889                 html: this.boxLabel
15890                 
15891             });
15892         }
15893         
15894         
15895        
15896         return cfg;
15897         
15898     },
15899     
15900     /**
15901      * return the real input element.
15902      */
15903     inputEl: function ()
15904     {
15905         return this.el.select('input.roo-checkbox',true).first();
15906     },
15907     
15908     label: function()
15909     {
15910         return this.el.select('label.control-label',true).first();
15911     },
15912     
15913     initEvents : function()
15914     {
15915 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
15916         
15917         this.inputEl().on('click', this.onClick,  this);
15918         
15919     },
15920     
15921     onClick : function()
15922     {   
15923         this.setChecked(!this.checked);
15924     },
15925     
15926     setChecked : function(state,suppressEvent)
15927     {
15928         this.checked = state;
15929         
15930         this.inputEl().dom.checked = state;
15931         
15932         if(suppressEvent !== true){
15933             this.fireEvent('check', this, state);
15934         }
15935         
15936         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
15937         
15938     },
15939     
15940     setValue : function(v,suppressEvent)
15941     {
15942         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
15943     }
15944     
15945 });
15946
15947  
15948 /*
15949  * - LGPL
15950  *
15951  * Radio
15952  * 
15953  */
15954
15955 /**
15956  * @class Roo.bootstrap.Radio
15957  * @extends Roo.bootstrap.CheckBox
15958  * Bootstrap Radio class
15959
15960  * @constructor
15961  * Create a new Radio
15962  * @param {Object} config The config object
15963  */
15964
15965 Roo.bootstrap.Radio = function(config){
15966     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
15967    
15968 };
15969
15970 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
15971     
15972     inputType: 'radio',
15973     inputValue: '',
15974     valueOff: '',
15975     
15976     getAutoCreate : function()
15977     {
15978         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
15979         
15980         var id = Roo.id();
15981         
15982         var cfg = {};
15983         
15984         cfg.cls = 'form-group radio' //input-group
15985         
15986         var input =  {
15987             tag: 'input',
15988             id : id,
15989             type : this.inputType,
15990             value : (!this.checked) ? this.valueOff : this.inputValue,
15991             cls : 'roo-radio',
15992             placeholder : this.placeholder || ''
15993             
15994         };
15995           if (this.weight) { // Validity check?
15996             cfg.cls += " radio-" + this.weight;
15997         }
15998         if (this.disabled) {
15999             input.disabled=true;
16000         }
16001         
16002         if(this.checked){
16003             input.checked = this.checked;
16004         }
16005         
16006         if (this.name) {
16007             input.name = this.name;
16008         }
16009         
16010         if (this.size) {
16011             input.cls += ' input-' + this.size;
16012         }
16013         
16014         var settings=this;
16015         ['xs','sm','md','lg'].map(function(size){
16016             if (settings[size]) {
16017                 cfg.cls += ' col-' + size + '-' + settings[size];
16018             }
16019         });
16020         
16021         var inputblock = input;
16022         
16023         if (this.before || this.after) {
16024             
16025             inputblock = {
16026                 cls : 'input-group',
16027                 cn :  [] 
16028             };
16029             if (this.before) {
16030                 inputblock.cn.push({
16031                     tag :'span',
16032                     cls : 'input-group-addon',
16033                     html : this.before
16034                 });
16035             }
16036             inputblock.cn.push(input);
16037             if (this.after) {
16038                 inputblock.cn.push({
16039                     tag :'span',
16040                     cls : 'input-group-addon',
16041                     html : this.after
16042                 });
16043             }
16044             
16045         };
16046         
16047         if (align ==='left' && this.fieldLabel.length) {
16048                 Roo.log("left and has label");
16049                 cfg.cn = [
16050                     
16051                     {
16052                         tag: 'label',
16053                         'for' :  id,
16054                         cls : 'control-label col-md-' + this.labelWidth,
16055                         html : this.fieldLabel
16056                         
16057                     },
16058                     {
16059                         cls : "col-md-" + (12 - this.labelWidth), 
16060                         cn: [
16061                             inputblock
16062                         ]
16063                     }
16064                     
16065                 ];
16066         } else if ( this.fieldLabel.length) {
16067                 Roo.log(" label");
16068                  cfg.cn = [
16069                    
16070                     {
16071                         tag: 'label',
16072                         'for': id,
16073                         cls: 'control-label box-input-label',
16074                         //cls : 'input-group-addon',
16075                         html : this.fieldLabel
16076                         
16077                     },
16078                     
16079                     inputblock
16080                     
16081                 ];
16082
16083         } else {
16084             
16085                    Roo.log(" no label && no align");
16086                 cfg.cn = [
16087                     
16088                         inputblock
16089                     
16090                 ];
16091                 
16092                 
16093         };
16094         
16095         if(this.boxLabel){
16096             cfg.cn.push({
16097                 tag: 'label',
16098                 'for': id,
16099                 cls: 'box-label',
16100                 html: this.boxLabel
16101             })
16102         }
16103         
16104         return cfg;
16105         
16106     },
16107     inputEl: function ()
16108     {
16109         return this.el.select('input.roo-radio',true).first();
16110     },
16111     onClick : function()
16112     {   
16113         this.setChecked(true);
16114     },
16115     
16116     setChecked : function(state,suppressEvent)
16117     {
16118         if(state){
16119             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16120                 v.dom.checked = false;
16121             });
16122         }
16123         
16124         this.checked = state;
16125         this.inputEl().dom.checked = state;
16126         
16127         if(suppressEvent !== true){
16128             this.fireEvent('check', this, state);
16129         }
16130         
16131         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
16132         
16133     },
16134     
16135     getGroupValue : function()
16136     {
16137         var value = ''
16138         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16139             if(v.dom.checked == true){
16140                 value = v.dom.value;
16141             }
16142         });
16143         
16144         return value;
16145     },
16146     
16147     /**
16148      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
16149      * @return {Mixed} value The field value
16150      */
16151     getValue : function(){
16152         return this.getGroupValue();
16153     }
16154     
16155 });
16156
16157  
16158 //<script type="text/javascript">
16159
16160 /*
16161  * Based  Ext JS Library 1.1.1
16162  * Copyright(c) 2006-2007, Ext JS, LLC.
16163  * LGPL
16164  *
16165  */
16166  
16167 /**
16168  * @class Roo.HtmlEditorCore
16169  * @extends Roo.Component
16170  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
16171  *
16172  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
16173  */
16174
16175 Roo.HtmlEditorCore = function(config){
16176     
16177     
16178     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
16179     this.addEvents({
16180         /**
16181          * @event initialize
16182          * Fires when the editor is fully initialized (including the iframe)
16183          * @param {Roo.HtmlEditorCore} this
16184          */
16185         initialize: true,
16186         /**
16187          * @event activate
16188          * Fires when the editor is first receives the focus. Any insertion must wait
16189          * until after this event.
16190          * @param {Roo.HtmlEditorCore} this
16191          */
16192         activate: true,
16193          /**
16194          * @event beforesync
16195          * Fires before the textarea is updated with content from the editor iframe. Return false
16196          * to cancel the sync.
16197          * @param {Roo.HtmlEditorCore} this
16198          * @param {String} html
16199          */
16200         beforesync: true,
16201          /**
16202          * @event beforepush
16203          * Fires before the iframe editor is updated with content from the textarea. Return false
16204          * to cancel the push.
16205          * @param {Roo.HtmlEditorCore} this
16206          * @param {String} html
16207          */
16208         beforepush: true,
16209          /**
16210          * @event sync
16211          * Fires when the textarea is updated with content from the editor iframe.
16212          * @param {Roo.HtmlEditorCore} this
16213          * @param {String} html
16214          */
16215         sync: true,
16216          /**
16217          * @event push
16218          * Fires when the iframe editor is updated with content from the textarea.
16219          * @param {Roo.HtmlEditorCore} this
16220          * @param {String} html
16221          */
16222         push: true,
16223         
16224         /**
16225          * @event editorevent
16226          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
16227          * @param {Roo.HtmlEditorCore} this
16228          */
16229         editorevent: true
16230     });
16231      
16232 };
16233
16234
16235 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
16236
16237
16238      /**
16239      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
16240      */
16241     
16242     owner : false,
16243     
16244      /**
16245      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
16246      *                        Roo.resizable.
16247      */
16248     resizable : false,
16249      /**
16250      * @cfg {Number} height (in pixels)
16251      */   
16252     height: 300,
16253    /**
16254      * @cfg {Number} width (in pixels)
16255      */   
16256     width: 500,
16257     
16258     /**
16259      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
16260      * 
16261      */
16262     stylesheets: false,
16263     
16264     // id of frame..
16265     frameId: false,
16266     
16267     // private properties
16268     validationEvent : false,
16269     deferHeight: true,
16270     initialized : false,
16271     activated : false,
16272     sourceEditMode : false,
16273     onFocus : Roo.emptyFn,
16274     iframePad:3,
16275     hideMode:'offsets',
16276     
16277     clearUp: true,
16278     
16279      
16280     
16281
16282     /**
16283      * Protected method that will not generally be called directly. It
16284      * is called when the editor initializes the iframe with HTML contents. Override this method if you
16285      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
16286      */
16287     getDocMarkup : function(){
16288         // body styles..
16289         var st = '';
16290         Roo.log(this.stylesheets);
16291         
16292         // inherit styels from page...?? 
16293         if (this.stylesheets === false) {
16294             
16295             Roo.get(document.head).select('style').each(function(node) {
16296                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16297             });
16298             
16299             Roo.get(document.head).select('link').each(function(node) { 
16300                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16301             });
16302             
16303         } else if (!this.stylesheets.length) {
16304                 // simple..
16305                 st = '<style type="text/css">' +
16306                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16307                    '</style>';
16308         } else {
16309             Roo.each(this.stylesheets, function(s) {
16310                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
16311             });
16312             
16313         }
16314         
16315         st +=  '<style type="text/css">' +
16316             'IMG { cursor: pointer } ' +
16317         '</style>';
16318
16319         
16320         return '<html><head>' + st  +
16321             //<style type="text/css">' +
16322             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16323             //'</style>' +
16324             ' </head><body class="roo-htmleditor-body"></body></html>';
16325     },
16326
16327     // private
16328     onRender : function(ct, position)
16329     {
16330         var _t = this;
16331         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
16332         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
16333         
16334         
16335         this.el.dom.style.border = '0 none';
16336         this.el.dom.setAttribute('tabIndex', -1);
16337         this.el.addClass('x-hidden hide');
16338         
16339         
16340         
16341         if(Roo.isIE){ // fix IE 1px bogus margin
16342             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
16343         }
16344        
16345         
16346         this.frameId = Roo.id();
16347         
16348          
16349         
16350         var iframe = this.owner.wrap.createChild({
16351             tag: 'iframe',
16352             cls: 'form-control', // bootstrap..
16353             id: this.frameId,
16354             name: this.frameId,
16355             frameBorder : 'no',
16356             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
16357         }, this.el
16358         );
16359         
16360         
16361         this.iframe = iframe.dom;
16362
16363          this.assignDocWin();
16364         
16365         this.doc.designMode = 'on';
16366        
16367         this.doc.open();
16368         this.doc.write(this.getDocMarkup());
16369         this.doc.close();
16370
16371         
16372         var task = { // must defer to wait for browser to be ready
16373             run : function(){
16374                 //console.log("run task?" + this.doc.readyState);
16375                 this.assignDocWin();
16376                 if(this.doc.body || this.doc.readyState == 'complete'){
16377                     try {
16378                         this.doc.designMode="on";
16379                     } catch (e) {
16380                         return;
16381                     }
16382                     Roo.TaskMgr.stop(task);
16383                     this.initEditor.defer(10, this);
16384                 }
16385             },
16386             interval : 10,
16387             duration: 10000,
16388             scope: this
16389         };
16390         Roo.TaskMgr.start(task);
16391
16392         
16393          
16394     },
16395
16396     // private
16397     onResize : function(w, h)
16398     {
16399          Roo.log('resize: ' +w + ',' + h );
16400         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
16401         if(!this.iframe){
16402             return;
16403         }
16404         if(typeof w == 'number'){
16405             
16406             this.iframe.style.width = w + 'px';
16407         }
16408         if(typeof h == 'number'){
16409             
16410             this.iframe.style.height = h + 'px';
16411             if(this.doc){
16412                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
16413             }
16414         }
16415         
16416     },
16417
16418     /**
16419      * Toggles the editor between standard and source edit mode.
16420      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
16421      */
16422     toggleSourceEdit : function(sourceEditMode){
16423         
16424         this.sourceEditMode = sourceEditMode === true;
16425         
16426         if(this.sourceEditMode){
16427  
16428             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
16429             
16430         }else{
16431             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
16432             //this.iframe.className = '';
16433             this.deferFocus();
16434         }
16435         //this.setSize(this.owner.wrap.getSize());
16436         //this.fireEvent('editmodechange', this, this.sourceEditMode);
16437     },
16438
16439     
16440   
16441
16442     /**
16443      * Protected method that will not generally be called directly. If you need/want
16444      * custom HTML cleanup, this is the method you should override.
16445      * @param {String} html The HTML to be cleaned
16446      * return {String} The cleaned HTML
16447      */
16448     cleanHtml : function(html){
16449         html = String(html);
16450         if(html.length > 5){
16451             if(Roo.isSafari){ // strip safari nonsense
16452                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
16453             }
16454         }
16455         if(html == '&nbsp;'){
16456             html = '';
16457         }
16458         return html;
16459     },
16460
16461     /**
16462      * HTML Editor -> Textarea
16463      * Protected method that will not generally be called directly. Syncs the contents
16464      * of the editor iframe with the textarea.
16465      */
16466     syncValue : function(){
16467         if(this.initialized){
16468             var bd = (this.doc.body || this.doc.documentElement);
16469             //this.cleanUpPaste(); -- this is done else where and causes havoc..
16470             var html = bd.innerHTML;
16471             if(Roo.isSafari){
16472                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
16473                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
16474                 if(m && m[1]){
16475                     html = '<div style="'+m[0]+'">' + html + '</div>';
16476                 }
16477             }
16478             html = this.cleanHtml(html);
16479             // fix up the special chars.. normaly like back quotes in word...
16480             // however we do not want to do this with chinese..
16481             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
16482                 var cc = b.charCodeAt();
16483                 if (
16484                     (cc >= 0x4E00 && cc < 0xA000 ) ||
16485                     (cc >= 0x3400 && cc < 0x4E00 ) ||
16486                     (cc >= 0xf900 && cc < 0xfb00 )
16487                 ) {
16488                         return b;
16489                 }
16490                 return "&#"+cc+";" 
16491             });
16492             if(this.owner.fireEvent('beforesync', this, html) !== false){
16493                 this.el.dom.value = html;
16494                 this.owner.fireEvent('sync', this, html);
16495             }
16496         }
16497     },
16498
16499     /**
16500      * Protected method that will not generally be called directly. Pushes the value of the textarea
16501      * into the iframe editor.
16502      */
16503     pushValue : function(){
16504         if(this.initialized){
16505             var v = this.el.dom.value.trim();
16506             
16507 //            if(v.length < 1){
16508 //                v = '&#160;';
16509 //            }
16510             
16511             if(this.owner.fireEvent('beforepush', this, v) !== false){
16512                 var d = (this.doc.body || this.doc.documentElement);
16513                 d.innerHTML = v;
16514                 this.cleanUpPaste();
16515                 this.el.dom.value = d.innerHTML;
16516                 this.owner.fireEvent('push', this, v);
16517             }
16518         }
16519     },
16520
16521     // private
16522     deferFocus : function(){
16523         this.focus.defer(10, this);
16524     },
16525
16526     // doc'ed in Field
16527     focus : function(){
16528         if(this.win && !this.sourceEditMode){
16529             this.win.focus();
16530         }else{
16531             this.el.focus();
16532         }
16533     },
16534     
16535     assignDocWin: function()
16536     {
16537         var iframe = this.iframe;
16538         
16539          if(Roo.isIE){
16540             this.doc = iframe.contentWindow.document;
16541             this.win = iframe.contentWindow;
16542         } else {
16543 //            if (!Roo.get(this.frameId)) {
16544 //                return;
16545 //            }
16546 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
16547 //            this.win = Roo.get(this.frameId).dom.contentWindow;
16548             
16549             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
16550                 return;
16551             }
16552             
16553             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
16554             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
16555         }
16556     },
16557     
16558     // private
16559     initEditor : function(){
16560         //console.log("INIT EDITOR");
16561         this.assignDocWin();
16562         
16563         
16564         
16565         this.doc.designMode="on";
16566         this.doc.open();
16567         this.doc.write(this.getDocMarkup());
16568         this.doc.close();
16569         
16570         var dbody = (this.doc.body || this.doc.documentElement);
16571         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
16572         // this copies styles from the containing element into thsi one..
16573         // not sure why we need all of this..
16574         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
16575         
16576         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
16577         //ss['background-attachment'] = 'fixed'; // w3c
16578         dbody.bgProperties = 'fixed'; // ie
16579         //Roo.DomHelper.applyStyles(dbody, ss);
16580         Roo.EventManager.on(this.doc, {
16581             //'mousedown': this.onEditorEvent,
16582             'mouseup': this.onEditorEvent,
16583             'dblclick': this.onEditorEvent,
16584             'click': this.onEditorEvent,
16585             'keyup': this.onEditorEvent,
16586             buffer:100,
16587             scope: this
16588         });
16589         if(Roo.isGecko){
16590             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
16591         }
16592         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
16593             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
16594         }
16595         this.initialized = true;
16596
16597         this.owner.fireEvent('initialize', this);
16598         this.pushValue();
16599     },
16600
16601     // private
16602     onDestroy : function(){
16603         
16604         
16605         
16606         if(this.rendered){
16607             
16608             //for (var i =0; i < this.toolbars.length;i++) {
16609             //    // fixme - ask toolbars for heights?
16610             //    this.toolbars[i].onDestroy();
16611            // }
16612             
16613             //this.wrap.dom.innerHTML = '';
16614             //this.wrap.remove();
16615         }
16616     },
16617
16618     // private
16619     onFirstFocus : function(){
16620         
16621         this.assignDocWin();
16622         
16623         
16624         this.activated = true;
16625          
16626     
16627         if(Roo.isGecko){ // prevent silly gecko errors
16628             this.win.focus();
16629             var s = this.win.getSelection();
16630             if(!s.focusNode || s.focusNode.nodeType != 3){
16631                 var r = s.getRangeAt(0);
16632                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
16633                 r.collapse(true);
16634                 this.deferFocus();
16635             }
16636             try{
16637                 this.execCmd('useCSS', true);
16638                 this.execCmd('styleWithCSS', false);
16639             }catch(e){}
16640         }
16641         this.owner.fireEvent('activate', this);
16642     },
16643
16644     // private
16645     adjustFont: function(btn){
16646         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
16647         //if(Roo.isSafari){ // safari
16648         //    adjust *= 2;
16649        // }
16650         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
16651         if(Roo.isSafari){ // safari
16652             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
16653             v =  (v < 10) ? 10 : v;
16654             v =  (v > 48) ? 48 : v;
16655             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
16656             
16657         }
16658         
16659         
16660         v = Math.max(1, v+adjust);
16661         
16662         this.execCmd('FontSize', v  );
16663     },
16664
16665     onEditorEvent : function(e){
16666         this.owner.fireEvent('editorevent', this, e);
16667       //  this.updateToolbar();
16668         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
16669     },
16670
16671     insertTag : function(tg)
16672     {
16673         // could be a bit smarter... -> wrap the current selected tRoo..
16674         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
16675             
16676             range = this.createRange(this.getSelection());
16677             var wrappingNode = this.doc.createElement(tg.toLowerCase());
16678             wrappingNode.appendChild(range.extractContents());
16679             range.insertNode(wrappingNode);
16680
16681             return;
16682             
16683             
16684             
16685         }
16686         this.execCmd("formatblock",   tg);
16687         
16688     },
16689     
16690     insertText : function(txt)
16691     {
16692         
16693         
16694         var range = this.createRange();
16695         range.deleteContents();
16696                //alert(Sender.getAttribute('label'));
16697                
16698         range.insertNode(this.doc.createTextNode(txt));
16699     } ,
16700     
16701      
16702
16703     /**
16704      * Executes a Midas editor command on the editor document and performs necessary focus and
16705      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
16706      * @param {String} cmd The Midas command
16707      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16708      */
16709     relayCmd : function(cmd, value){
16710         this.win.focus();
16711         this.execCmd(cmd, value);
16712         this.owner.fireEvent('editorevent', this);
16713         //this.updateToolbar();
16714         this.owner.deferFocus();
16715     },
16716
16717     /**
16718      * Executes a Midas editor command directly on the editor document.
16719      * For visual commands, you should use {@link #relayCmd} instead.
16720      * <b>This should only be called after the editor is initialized.</b>
16721      * @param {String} cmd The Midas command
16722      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16723      */
16724     execCmd : function(cmd, value){
16725         this.doc.execCommand(cmd, false, value === undefined ? null : value);
16726         this.syncValue();
16727     },
16728  
16729  
16730    
16731     /**
16732      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
16733      * to insert tRoo.
16734      * @param {String} text | dom node.. 
16735      */
16736     insertAtCursor : function(text)
16737     {
16738         
16739         
16740         
16741         if(!this.activated){
16742             return;
16743         }
16744         /*
16745         if(Roo.isIE){
16746             this.win.focus();
16747             var r = this.doc.selection.createRange();
16748             if(r){
16749                 r.collapse(true);
16750                 r.pasteHTML(text);
16751                 this.syncValue();
16752                 this.deferFocus();
16753             
16754             }
16755             return;
16756         }
16757         */
16758         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
16759             this.win.focus();
16760             
16761             
16762             // from jquery ui (MIT licenced)
16763             var range, node;
16764             var win = this.win;
16765             
16766             if (win.getSelection && win.getSelection().getRangeAt) {
16767                 range = win.getSelection().getRangeAt(0);
16768                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
16769                 range.insertNode(node);
16770             } else if (win.document.selection && win.document.selection.createRange) {
16771                 // no firefox support
16772                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
16773                 win.document.selection.createRange().pasteHTML(txt);
16774             } else {
16775                 // no firefox support
16776                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
16777                 this.execCmd('InsertHTML', txt);
16778             } 
16779             
16780             this.syncValue();
16781             
16782             this.deferFocus();
16783         }
16784     },
16785  // private
16786     mozKeyPress : function(e){
16787         if(e.ctrlKey){
16788             var c = e.getCharCode(), cmd;
16789           
16790             if(c > 0){
16791                 c = String.fromCharCode(c).toLowerCase();
16792                 switch(c){
16793                     case 'b':
16794                         cmd = 'bold';
16795                         break;
16796                     case 'i':
16797                         cmd = 'italic';
16798                         break;
16799                     
16800                     case 'u':
16801                         cmd = 'underline';
16802                         break;
16803                     
16804                     case 'v':
16805                         this.cleanUpPaste.defer(100, this);
16806                         return;
16807                         
16808                 }
16809                 if(cmd){
16810                     this.win.focus();
16811                     this.execCmd(cmd);
16812                     this.deferFocus();
16813                     e.preventDefault();
16814                 }
16815                 
16816             }
16817         }
16818     },
16819
16820     // private
16821     fixKeys : function(){ // load time branching for fastest keydown performance
16822         if(Roo.isIE){
16823             return function(e){
16824                 var k = e.getKey(), r;
16825                 if(k == e.TAB){
16826                     e.stopEvent();
16827                     r = this.doc.selection.createRange();
16828                     if(r){
16829                         r.collapse(true);
16830                         r.pasteHTML('&#160;&#160;&#160;&#160;');
16831                         this.deferFocus();
16832                     }
16833                     return;
16834                 }
16835                 
16836                 if(k == e.ENTER){
16837                     r = this.doc.selection.createRange();
16838                     if(r){
16839                         var target = r.parentElement();
16840                         if(!target || target.tagName.toLowerCase() != 'li'){
16841                             e.stopEvent();
16842                             r.pasteHTML('<br />');
16843                             r.collapse(false);
16844                             r.select();
16845                         }
16846                     }
16847                 }
16848                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
16849                     this.cleanUpPaste.defer(100, this);
16850                     return;
16851                 }
16852                 
16853                 
16854             };
16855         }else if(Roo.isOpera){
16856             return function(e){
16857                 var k = e.getKey();
16858                 if(k == e.TAB){
16859                     e.stopEvent();
16860                     this.win.focus();
16861                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
16862                     this.deferFocus();
16863                 }
16864                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
16865                     this.cleanUpPaste.defer(100, this);
16866                     return;
16867                 }
16868                 
16869             };
16870         }else if(Roo.isSafari){
16871             return function(e){
16872                 var k = e.getKey();
16873                 
16874                 if(k == e.TAB){
16875                     e.stopEvent();
16876                     this.execCmd('InsertText','\t');
16877                     this.deferFocus();
16878                     return;
16879                 }
16880                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
16881                     this.cleanUpPaste.defer(100, this);
16882                     return;
16883                 }
16884                 
16885              };
16886         }
16887     }(),
16888     
16889     getAllAncestors: function()
16890     {
16891         var p = this.getSelectedNode();
16892         var a = [];
16893         if (!p) {
16894             a.push(p); // push blank onto stack..
16895             p = this.getParentElement();
16896         }
16897         
16898         
16899         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
16900             a.push(p);
16901             p = p.parentNode;
16902         }
16903         a.push(this.doc.body);
16904         return a;
16905     },
16906     lastSel : false,
16907     lastSelNode : false,
16908     
16909     
16910     getSelection : function() 
16911     {
16912         this.assignDocWin();
16913         return Roo.isIE ? this.doc.selection : this.win.getSelection();
16914     },
16915     
16916     getSelectedNode: function() 
16917     {
16918         // this may only work on Gecko!!!
16919         
16920         // should we cache this!!!!
16921         
16922         
16923         
16924          
16925         var range = this.createRange(this.getSelection()).cloneRange();
16926         
16927         if (Roo.isIE) {
16928             var parent = range.parentElement();
16929             while (true) {
16930                 var testRange = range.duplicate();
16931                 testRange.moveToElementText(parent);
16932                 if (testRange.inRange(range)) {
16933                     break;
16934                 }
16935                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
16936                     break;
16937                 }
16938                 parent = parent.parentElement;
16939             }
16940             return parent;
16941         }
16942         
16943         // is ancestor a text element.
16944         var ac =  range.commonAncestorContainer;
16945         if (ac.nodeType == 3) {
16946             ac = ac.parentNode;
16947         }
16948         
16949         var ar = ac.childNodes;
16950          
16951         var nodes = [];
16952         var other_nodes = [];
16953         var has_other_nodes = false;
16954         for (var i=0;i<ar.length;i++) {
16955             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
16956                 continue;
16957             }
16958             // fullly contained node.
16959             
16960             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
16961                 nodes.push(ar[i]);
16962                 continue;
16963             }
16964             
16965             // probably selected..
16966             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
16967                 other_nodes.push(ar[i]);
16968                 continue;
16969             }
16970             // outer..
16971             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
16972                 continue;
16973             }
16974             
16975             
16976             has_other_nodes = true;
16977         }
16978         if (!nodes.length && other_nodes.length) {
16979             nodes= other_nodes;
16980         }
16981         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
16982             return false;
16983         }
16984         
16985         return nodes[0];
16986     },
16987     createRange: function(sel)
16988     {
16989         // this has strange effects when using with 
16990         // top toolbar - not sure if it's a great idea.
16991         //this.editor.contentWindow.focus();
16992         if (typeof sel != "undefined") {
16993             try {
16994                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
16995             } catch(e) {
16996                 return this.doc.createRange();
16997             }
16998         } else {
16999             return this.doc.createRange();
17000         }
17001     },
17002     getParentElement: function()
17003     {
17004         
17005         this.assignDocWin();
17006         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
17007         
17008         var range = this.createRange(sel);
17009          
17010         try {
17011             var p = range.commonAncestorContainer;
17012             while (p.nodeType == 3) { // text node
17013                 p = p.parentNode;
17014             }
17015             return p;
17016         } catch (e) {
17017             return null;
17018         }
17019     
17020     },
17021     /***
17022      *
17023      * Range intersection.. the hard stuff...
17024      *  '-1' = before
17025      *  '0' = hits..
17026      *  '1' = after.
17027      *         [ -- selected range --- ]
17028      *   [fail]                        [fail]
17029      *
17030      *    basically..
17031      *      if end is before start or  hits it. fail.
17032      *      if start is after end or hits it fail.
17033      *
17034      *   if either hits (but other is outside. - then it's not 
17035      *   
17036      *    
17037      **/
17038     
17039     
17040     // @see http://www.thismuchiknow.co.uk/?p=64.
17041     rangeIntersectsNode : function(range, node)
17042     {
17043         var nodeRange = node.ownerDocument.createRange();
17044         try {
17045             nodeRange.selectNode(node);
17046         } catch (e) {
17047             nodeRange.selectNodeContents(node);
17048         }
17049     
17050         var rangeStartRange = range.cloneRange();
17051         rangeStartRange.collapse(true);
17052     
17053         var rangeEndRange = range.cloneRange();
17054         rangeEndRange.collapse(false);
17055     
17056         var nodeStartRange = nodeRange.cloneRange();
17057         nodeStartRange.collapse(true);
17058     
17059         var nodeEndRange = nodeRange.cloneRange();
17060         nodeEndRange.collapse(false);
17061     
17062         return rangeStartRange.compareBoundaryPoints(
17063                  Range.START_TO_START, nodeEndRange) == -1 &&
17064                rangeEndRange.compareBoundaryPoints(
17065                  Range.START_TO_START, nodeStartRange) == 1;
17066         
17067          
17068     },
17069     rangeCompareNode : function(range, node)
17070     {
17071         var nodeRange = node.ownerDocument.createRange();
17072         try {
17073             nodeRange.selectNode(node);
17074         } catch (e) {
17075             nodeRange.selectNodeContents(node);
17076         }
17077         
17078         
17079         range.collapse(true);
17080     
17081         nodeRange.collapse(true);
17082      
17083         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
17084         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
17085          
17086         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
17087         
17088         var nodeIsBefore   =  ss == 1;
17089         var nodeIsAfter    = ee == -1;
17090         
17091         if (nodeIsBefore && nodeIsAfter)
17092             return 0; // outer
17093         if (!nodeIsBefore && nodeIsAfter)
17094             return 1; //right trailed.
17095         
17096         if (nodeIsBefore && !nodeIsAfter)
17097             return 2;  // left trailed.
17098         // fully contined.
17099         return 3;
17100     },
17101
17102     // private? - in a new class?
17103     cleanUpPaste :  function()
17104     {
17105         // cleans up the whole document..
17106         Roo.log('cleanuppaste');
17107         
17108         this.cleanUpChildren(this.doc.body);
17109         var clean = this.cleanWordChars(this.doc.body.innerHTML);
17110         if (clean != this.doc.body.innerHTML) {
17111             this.doc.body.innerHTML = clean;
17112         }
17113         
17114     },
17115     
17116     cleanWordChars : function(input) {// change the chars to hex code
17117         var he = Roo.HtmlEditorCore;
17118         
17119         var output = input;
17120         Roo.each(he.swapCodes, function(sw) { 
17121             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
17122             
17123             output = output.replace(swapper, sw[1]);
17124         });
17125         
17126         return output;
17127     },
17128     
17129     
17130     cleanUpChildren : function (n)
17131     {
17132         if (!n.childNodes.length) {
17133             return;
17134         }
17135         for (var i = n.childNodes.length-1; i > -1 ; i--) {
17136            this.cleanUpChild(n.childNodes[i]);
17137         }
17138     },
17139     
17140     
17141         
17142     
17143     cleanUpChild : function (node)
17144     {
17145         var ed = this;
17146         //console.log(node);
17147         if (node.nodeName == "#text") {
17148             // clean up silly Windows -- stuff?
17149             return; 
17150         }
17151         if (node.nodeName == "#comment") {
17152             node.parentNode.removeChild(node);
17153             // clean up silly Windows -- stuff?
17154             return; 
17155         }
17156         
17157         if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1 && this.clearUp) {
17158             // remove node.
17159             node.parentNode.removeChild(node);
17160             return;
17161             
17162         }
17163         
17164         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
17165         
17166         // remove <a name=....> as rendering on yahoo mailer is borked with this.
17167         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
17168         
17169         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
17170         //    remove_keep_children = true;
17171         //}
17172         
17173         if (remove_keep_children) {
17174             this.cleanUpChildren(node);
17175             // inserts everything just before this node...
17176             while (node.childNodes.length) {
17177                 var cn = node.childNodes[0];
17178                 node.removeChild(cn);
17179                 node.parentNode.insertBefore(cn, node);
17180             }
17181             node.parentNode.removeChild(node);
17182             return;
17183         }
17184         
17185         if (!node.attributes || !node.attributes.length) {
17186             this.cleanUpChildren(node);
17187             return;
17188         }
17189         
17190         function cleanAttr(n,v)
17191         {
17192             
17193             if (v.match(/^\./) || v.match(/^\//)) {
17194                 return;
17195             }
17196             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
17197                 return;
17198             }
17199             if (v.match(/^#/)) {
17200                 return;
17201             }
17202 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
17203             node.removeAttribute(n);
17204             
17205         }
17206         
17207         function cleanStyle(n,v)
17208         {
17209             if (v.match(/expression/)) { //XSS?? should we even bother..
17210                 node.removeAttribute(n);
17211                 return;
17212             }
17213             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
17214             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
17215             
17216             
17217             var parts = v.split(/;/);
17218             var clean = [];
17219             
17220             Roo.each(parts, function(p) {
17221                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
17222                 if (!p.length) {
17223                     return true;
17224                 }
17225                 var l = p.split(':').shift().replace(/\s+/g,'');
17226                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
17227                 
17228                 if ( cblack.indexOf(l) > -1) {
17229 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17230                     //node.removeAttribute(n);
17231                     return true;
17232                 }
17233                 //Roo.log()
17234                 // only allow 'c whitelisted system attributes'
17235                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
17236 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17237                     //node.removeAttribute(n);
17238                     return true;
17239                 }
17240                 
17241                 
17242                  
17243                 
17244                 clean.push(p);
17245                 return true;
17246             });
17247             if (clean.length) { 
17248                 node.setAttribute(n, clean.join(';'));
17249             } else {
17250                 node.removeAttribute(n);
17251             }
17252             
17253         }
17254         
17255         
17256         for (var i = node.attributes.length-1; i > -1 ; i--) {
17257             var a = node.attributes[i];
17258             //console.log(a);
17259             
17260             if (a.name.toLowerCase().substr(0,2)=='on')  {
17261                 node.removeAttribute(a.name);
17262                 continue;
17263             }
17264             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
17265                 node.removeAttribute(a.name);
17266                 continue;
17267             }
17268             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
17269                 cleanAttr(a.name,a.value); // fixme..
17270                 continue;
17271             }
17272             if (a.name == 'style') {
17273                 cleanStyle(a.name,a.value);
17274                 continue;
17275             }
17276             /// clean up MS crap..
17277             // tecnically this should be a list of valid class'es..
17278             
17279             
17280             if (a.name == 'class') {
17281                 if (a.value.match(/^Mso/)) {
17282                     node.className = '';
17283                 }
17284                 
17285                 if (a.value.match(/body/)) {
17286                     node.className = '';
17287                 }
17288                 continue;
17289             }
17290             
17291             // style cleanup!?
17292             // class cleanup?
17293             
17294         }
17295         
17296         
17297         this.cleanUpChildren(node);
17298         
17299         
17300     },
17301     /**
17302      * Clean up MS wordisms...
17303      */
17304     cleanWord : function(node)
17305     {
17306         var _t = this;
17307         var cleanWordChildren = function()
17308         {
17309             if (!node.childNodes.length) {
17310                 return;
17311             }
17312             for (var i = node.childNodes.length-1; i > -1 ; i--) {
17313                _t.cleanWord(node.childNodes[i]);
17314             }
17315         }
17316         
17317         
17318         if (!node) {
17319             this.cleanWord(this.doc.body);
17320             return;
17321         }
17322         if (node.nodeName == "#text") {
17323             // clean up silly Windows -- stuff?
17324             return; 
17325         }
17326         if (node.nodeName == "#comment") {
17327             node.parentNode.removeChild(node);
17328             // clean up silly Windows -- stuff?
17329             return; 
17330         }
17331         
17332         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
17333             node.parentNode.removeChild(node);
17334             return;
17335         }
17336         
17337         // remove - but keep children..
17338         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
17339             while (node.childNodes.length) {
17340                 var cn = node.childNodes[0];
17341                 node.removeChild(cn);
17342                 node.parentNode.insertBefore(cn, node);
17343             }
17344             node.parentNode.removeChild(node);
17345             cleanWordChildren();
17346             return;
17347         }
17348         // clean styles
17349         if (node.className.length) {
17350             
17351             var cn = node.className.split(/\W+/);
17352             var cna = [];
17353             Roo.each(cn, function(cls) {
17354                 if (cls.match(/Mso[a-zA-Z]+/)) {
17355                     return;
17356                 }
17357                 cna.push(cls);
17358             });
17359             node.className = cna.length ? cna.join(' ') : '';
17360             if (!cna.length) {
17361                 node.removeAttribute("class");
17362             }
17363         }
17364         
17365         if (node.hasAttribute("lang")) {
17366             node.removeAttribute("lang");
17367         }
17368         
17369         if (node.hasAttribute("style")) {
17370             
17371             var styles = node.getAttribute("style").split(";");
17372             var nstyle = [];
17373             Roo.each(styles, function(s) {
17374                 if (!s.match(/:/)) {
17375                     return;
17376                 }
17377                 var kv = s.split(":");
17378                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
17379                     return;
17380                 }
17381                 // what ever is left... we allow.
17382                 nstyle.push(s);
17383             });
17384             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
17385             if (!nstyle.length) {
17386                 node.removeAttribute('style');
17387             }
17388         }
17389         
17390         cleanWordChildren();
17391         
17392         
17393     },
17394     domToHTML : function(currentElement, depth, nopadtext) {
17395         
17396             depth = depth || 0;
17397             nopadtext = nopadtext || false;
17398         
17399             if (!currentElement) {
17400                 return this.domToHTML(this.doc.body);
17401             }
17402             
17403             //Roo.log(currentElement);
17404             var j;
17405             var allText = false;
17406             var nodeName = currentElement.nodeName;
17407             var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
17408             
17409             if  (nodeName == '#text') {
17410                 return currentElement.nodeValue;
17411             }
17412             
17413             
17414             var ret = '';
17415             if (nodeName != 'BODY') {
17416                  
17417                 var i = 0;
17418                 // Prints the node tagName, such as <A>, <IMG>, etc
17419                 if (tagName) {
17420                     var attr = [];
17421                     for(i = 0; i < currentElement.attributes.length;i++) {
17422                         // quoting?
17423                         var aname = currentElement.attributes.item(i).name;
17424                         if (!currentElement.attributes.item(i).value.length) {
17425                             continue;
17426                         }
17427                         attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
17428                     }
17429                     
17430                     ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
17431                 } 
17432                 else {
17433                     
17434                     // eack
17435                 }
17436             } else {
17437                 tagName = false;
17438             }
17439             if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
17440                 return ret;
17441             }
17442             if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
17443                 nopadtext = true;
17444             }
17445             
17446             
17447             // Traverse the tree
17448             i = 0;
17449             var currentElementChild = currentElement.childNodes.item(i);
17450             var allText = true;
17451             var innerHTML  = '';
17452             lastnode = '';
17453             while (currentElementChild) {
17454                 // Formatting code (indent the tree so it looks nice on the screen)
17455                 var nopad = nopadtext;
17456                 if (lastnode == 'SPAN') {
17457                     nopad  = true;
17458                 }
17459                 // text
17460                 if  (currentElementChild.nodeName == '#text') {
17461                     var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
17462                     if (!nopad && toadd.length > 80) {
17463                         innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
17464                     }
17465                     innerHTML  += toadd;
17466                     
17467                     i++;
17468                     currentElementChild = currentElement.childNodes.item(i);
17469                     lastNode = '';
17470                     continue;
17471                 }
17472                 allText = false;
17473                 
17474                 innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
17475                     
17476                 // Recursively traverse the tree structure of the child node
17477                 innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
17478                 lastnode = currentElementChild.nodeName;
17479                 i++;
17480                 currentElementChild=currentElement.childNodes.item(i);
17481             }
17482             
17483             ret += innerHTML;
17484             
17485             if (!allText) {
17486                     // The remaining code is mostly for formatting the tree
17487                 ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
17488             }
17489             
17490             
17491             if (tagName) {
17492                 ret+= "</"+tagName+">";
17493             }
17494             return ret;
17495             
17496         }
17497     
17498     // hide stuff that is not compatible
17499     /**
17500      * @event blur
17501      * @hide
17502      */
17503     /**
17504      * @event change
17505      * @hide
17506      */
17507     /**
17508      * @event focus
17509      * @hide
17510      */
17511     /**
17512      * @event specialkey
17513      * @hide
17514      */
17515     /**
17516      * @cfg {String} fieldClass @hide
17517      */
17518     /**
17519      * @cfg {String} focusClass @hide
17520      */
17521     /**
17522      * @cfg {String} autoCreate @hide
17523      */
17524     /**
17525      * @cfg {String} inputType @hide
17526      */
17527     /**
17528      * @cfg {String} invalidClass @hide
17529      */
17530     /**
17531      * @cfg {String} invalidText @hide
17532      */
17533     /**
17534      * @cfg {String} msgFx @hide
17535      */
17536     /**
17537      * @cfg {String} validateOnBlur @hide
17538      */
17539 });
17540
17541 Roo.HtmlEditorCore.white = [
17542         'area', 'br', 'img', 'input', 'hr', 'wbr',
17543         
17544        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
17545        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
17546        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
17547        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
17548        'table',   'ul',         'xmp', 
17549        
17550        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
17551       'thead',   'tr', 
17552      
17553       'dir', 'menu', 'ol', 'ul', 'dl',
17554        
17555       'embed',  'object'
17556 ];
17557
17558
17559 Roo.HtmlEditorCore.black = [
17560     //    'embed',  'object', // enable - backend responsiblity to clean thiese
17561         'applet', // 
17562         'base',   'basefont', 'bgsound', 'blink',  'body', 
17563         'frame',  'frameset', 'head',    'html',   'ilayer', 
17564         'iframe', 'layer',  'link',     'meta',    'object',   
17565         'script', 'style' ,'title',  'xml' // clean later..
17566 ];
17567 Roo.HtmlEditorCore.clean = [
17568     'script', 'style', 'title', 'xml'
17569 ];
17570 Roo.HtmlEditorCore.remove = [
17571     'font'
17572 ];
17573 // attributes..
17574
17575 Roo.HtmlEditorCore.ablack = [
17576     'on'
17577 ];
17578     
17579 Roo.HtmlEditorCore.aclean = [ 
17580     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
17581 ];
17582
17583 // protocols..
17584 Roo.HtmlEditorCore.pwhite= [
17585         'http',  'https',  'mailto'
17586 ];
17587
17588 // white listed style attributes.
17589 Roo.HtmlEditorCore.cwhite= [
17590       //  'text-align', /// default is to allow most things..
17591       
17592          
17593 //        'font-size'//??
17594 ];
17595
17596 // black listed style attributes.
17597 Roo.HtmlEditorCore.cblack= [
17598       //  'font-size' -- this can be set by the project 
17599 ];
17600
17601
17602 Roo.HtmlEditorCore.swapCodes   =[ 
17603     [    8211, "--" ], 
17604     [    8212, "--" ], 
17605     [    8216,  "'" ],  
17606     [    8217, "'" ],  
17607     [    8220, '"' ],  
17608     [    8221, '"' ],  
17609     [    8226, "*" ],  
17610     [    8230, "..." ]
17611 ]; 
17612
17613     /*
17614  * - LGPL
17615  *
17616  * HtmlEditor
17617  * 
17618  */
17619
17620 /**
17621  * @class Roo.bootstrap.HtmlEditor
17622  * @extends Roo.bootstrap.TextArea
17623  * Bootstrap HtmlEditor class
17624
17625  * @constructor
17626  * Create a new HtmlEditor
17627  * @param {Object} config The config object
17628  */
17629
17630 Roo.bootstrap.HtmlEditor = function(config){
17631     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
17632     if (!this.toolbars) {
17633         this.toolbars = [];
17634     }
17635     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
17636     this.addEvents({
17637             /**
17638              * @event initialize
17639              * Fires when the editor is fully initialized (including the iframe)
17640              * @param {HtmlEditor} this
17641              */
17642             initialize: true,
17643             /**
17644              * @event activate
17645              * Fires when the editor is first receives the focus. Any insertion must wait
17646              * until after this event.
17647              * @param {HtmlEditor} this
17648              */
17649             activate: true,
17650              /**
17651              * @event beforesync
17652              * Fires before the textarea is updated with content from the editor iframe. Return false
17653              * to cancel the sync.
17654              * @param {HtmlEditor} this
17655              * @param {String} html
17656              */
17657             beforesync: true,
17658              /**
17659              * @event beforepush
17660              * Fires before the iframe editor is updated with content from the textarea. Return false
17661              * to cancel the push.
17662              * @param {HtmlEditor} this
17663              * @param {String} html
17664              */
17665             beforepush: true,
17666              /**
17667              * @event sync
17668              * Fires when the textarea is updated with content from the editor iframe.
17669              * @param {HtmlEditor} this
17670              * @param {String} html
17671              */
17672             sync: true,
17673              /**
17674              * @event push
17675              * Fires when the iframe editor is updated with content from the textarea.
17676              * @param {HtmlEditor} this
17677              * @param {String} html
17678              */
17679             push: true,
17680              /**
17681              * @event editmodechange
17682              * Fires when the editor switches edit modes
17683              * @param {HtmlEditor} this
17684              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
17685              */
17686             editmodechange: true,
17687             /**
17688              * @event editorevent
17689              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
17690              * @param {HtmlEditor} this
17691              */
17692             editorevent: true,
17693             /**
17694              * @event firstfocus
17695              * Fires when on first focus - needed by toolbars..
17696              * @param {HtmlEditor} this
17697              */
17698             firstfocus: true,
17699             /**
17700              * @event autosave
17701              * Auto save the htmlEditor value as a file into Events
17702              * @param {HtmlEditor} this
17703              */
17704             autosave: true,
17705             /**
17706              * @event savedpreview
17707              * preview the saved version of htmlEditor
17708              * @param {HtmlEditor} this
17709              */
17710             savedpreview: true
17711         });
17712 };
17713
17714
17715 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
17716     
17717     
17718       /**
17719      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
17720      */
17721     toolbars : false,
17722    
17723      /**
17724      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
17725      *                        Roo.resizable.
17726      */
17727     resizable : false,
17728      /**
17729      * @cfg {Number} height (in pixels)
17730      */   
17731     height: 300,
17732    /**
17733      * @cfg {Number} width (in pixels)
17734      */   
17735     width: false,
17736     
17737     /**
17738      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
17739      * 
17740      */
17741     stylesheets: false,
17742     
17743     // id of frame..
17744     frameId: false,
17745     
17746     // private properties
17747     validationEvent : false,
17748     deferHeight: true,
17749     initialized : false,
17750     activated : false,
17751     
17752     onFocus : Roo.emptyFn,
17753     iframePad:3,
17754     hideMode:'offsets',
17755     
17756     
17757     tbContainer : false,
17758     
17759     toolbarContainer :function() {
17760         return this.wrap.select('.x-html-editor-tb',true).first();
17761     },
17762
17763     /**
17764      * Protected method that will not generally be called directly. It
17765      * is called when the editor creates its toolbar. Override this method if you need to
17766      * add custom toolbar buttons.
17767      * @param {HtmlEditor} editor
17768      */
17769     createToolbar : function(){
17770         
17771         Roo.log("create toolbars");
17772         
17773         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
17774         this.toolbars[0].render(this.toolbarContainer());
17775         
17776         return;
17777         
17778 //        if (!editor.toolbars || !editor.toolbars.length) {
17779 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
17780 //        }
17781 //        
17782 //        for (var i =0 ; i < editor.toolbars.length;i++) {
17783 //            editor.toolbars[i] = Roo.factory(
17784 //                    typeof(editor.toolbars[i]) == 'string' ?
17785 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
17786 //                Roo.bootstrap.HtmlEditor);
17787 //            editor.toolbars[i].init(editor);
17788 //        }
17789     },
17790
17791      
17792     // private
17793     onRender : function(ct, position)
17794     {
17795        // Roo.log("Call onRender: " + this.xtype);
17796         var _t = this;
17797         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
17798       
17799         this.wrap = this.inputEl().wrap({
17800             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
17801         });
17802         
17803         this.editorcore.onRender(ct, position);
17804          
17805         if (this.resizable) {
17806             this.resizeEl = new Roo.Resizable(this.wrap, {
17807                 pinned : true,
17808                 wrap: true,
17809                 dynamic : true,
17810                 minHeight : this.height,
17811                 height: this.height,
17812                 handles : this.resizable,
17813                 width: this.width,
17814                 listeners : {
17815                     resize : function(r, w, h) {
17816                         _t.onResize(w,h); // -something
17817                     }
17818                 }
17819             });
17820             
17821         }
17822         this.createToolbar(this);
17823        
17824         
17825         if(!this.width && this.resizable){
17826             this.setSize(this.wrap.getSize());
17827         }
17828         if (this.resizeEl) {
17829             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
17830             // should trigger onReize..
17831         }
17832         
17833     },
17834
17835     // private
17836     onResize : function(w, h)
17837     {
17838         Roo.log('resize: ' +w + ',' + h );
17839         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
17840         var ew = false;
17841         var eh = false;
17842         
17843         if(this.inputEl() ){
17844             if(typeof w == 'number'){
17845                 var aw = w - this.wrap.getFrameWidth('lr');
17846                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
17847                 ew = aw;
17848             }
17849             if(typeof h == 'number'){
17850                  var tbh = -11;  // fixme it needs to tool bar size!
17851                 for (var i =0; i < this.toolbars.length;i++) {
17852                     // fixme - ask toolbars for heights?
17853                     tbh += this.toolbars[i].el.getHeight();
17854                     //if (this.toolbars[i].footer) {
17855                     //    tbh += this.toolbars[i].footer.el.getHeight();
17856                     //}
17857                 }
17858               
17859                 
17860                 
17861                 
17862                 
17863                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
17864                 ah -= 5; // knock a few pixes off for look..
17865                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
17866                 var eh = ah;
17867             }
17868         }
17869         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
17870         this.editorcore.onResize(ew,eh);
17871         
17872     },
17873
17874     /**
17875      * Toggles the editor between standard and source edit mode.
17876      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
17877      */
17878     toggleSourceEdit : function(sourceEditMode)
17879     {
17880         this.editorcore.toggleSourceEdit(sourceEditMode);
17881         
17882         if(this.editorcore.sourceEditMode){
17883             Roo.log('editor - showing textarea');
17884             
17885 //            Roo.log('in');
17886 //            Roo.log(this.syncValue());
17887             this.syncValue();
17888             this.inputEl().removeClass(['hide', 'x-hidden']);
17889             this.inputEl().dom.removeAttribute('tabIndex');
17890             this.inputEl().focus();
17891         }else{
17892             Roo.log('editor - hiding textarea');
17893 //            Roo.log('out')
17894 //            Roo.log(this.pushValue()); 
17895             this.pushValue();
17896             
17897             this.inputEl().addClass(['hide', 'x-hidden']);
17898             this.inputEl().dom.setAttribute('tabIndex', -1);
17899             //this.deferFocus();
17900         }
17901          
17902         if(this.resizable){
17903             this.setSize(this.wrap.getSize());
17904         }
17905         
17906         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
17907     },
17908  
17909     // private (for BoxComponent)
17910     adjustSize : Roo.BoxComponent.prototype.adjustSize,
17911
17912     // private (for BoxComponent)
17913     getResizeEl : function(){
17914         return this.wrap;
17915     },
17916
17917     // private (for BoxComponent)
17918     getPositionEl : function(){
17919         return this.wrap;
17920     },
17921
17922     // private
17923     initEvents : function(){
17924         this.originalValue = this.getValue();
17925     },
17926
17927 //    /**
17928 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
17929 //     * @method
17930 //     */
17931 //    markInvalid : Roo.emptyFn,
17932 //    /**
17933 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
17934 //     * @method
17935 //     */
17936 //    clearInvalid : Roo.emptyFn,
17937
17938     setValue : function(v){
17939         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
17940         this.editorcore.pushValue();
17941     },
17942
17943      
17944     // private
17945     deferFocus : function(){
17946         this.focus.defer(10, this);
17947     },
17948
17949     // doc'ed in Field
17950     focus : function(){
17951         this.editorcore.focus();
17952         
17953     },
17954       
17955
17956     // private
17957     onDestroy : function(){
17958         
17959         
17960         
17961         if(this.rendered){
17962             
17963             for (var i =0; i < this.toolbars.length;i++) {
17964                 // fixme - ask toolbars for heights?
17965                 this.toolbars[i].onDestroy();
17966             }
17967             
17968             this.wrap.dom.innerHTML = '';
17969             this.wrap.remove();
17970         }
17971     },
17972
17973     // private
17974     onFirstFocus : function(){
17975         //Roo.log("onFirstFocus");
17976         this.editorcore.onFirstFocus();
17977          for (var i =0; i < this.toolbars.length;i++) {
17978             this.toolbars[i].onFirstFocus();
17979         }
17980         
17981     },
17982     
17983     // private
17984     syncValue : function()
17985     {   
17986         this.editorcore.syncValue();
17987     },
17988     
17989     pushValue : function()
17990     {   
17991         this.editorcore.pushValue();
17992     }
17993      
17994     
17995     // hide stuff that is not compatible
17996     /**
17997      * @event blur
17998      * @hide
17999      */
18000     /**
18001      * @event change
18002      * @hide
18003      */
18004     /**
18005      * @event focus
18006      * @hide
18007      */
18008     /**
18009      * @event specialkey
18010      * @hide
18011      */
18012     /**
18013      * @cfg {String} fieldClass @hide
18014      */
18015     /**
18016      * @cfg {String} focusClass @hide
18017      */
18018     /**
18019      * @cfg {String} autoCreate @hide
18020      */
18021     /**
18022      * @cfg {String} inputType @hide
18023      */
18024     /**
18025      * @cfg {String} invalidClass @hide
18026      */
18027     /**
18028      * @cfg {String} invalidText @hide
18029      */
18030     /**
18031      * @cfg {String} msgFx @hide
18032      */
18033     /**
18034      * @cfg {String} validateOnBlur @hide
18035      */
18036 });
18037  
18038     
18039    
18040    
18041    
18042       
18043 Roo.namespace('Roo.bootstrap.htmleditor');
18044 /**
18045  * @class Roo.bootstrap.HtmlEditorToolbar1
18046  * Basic Toolbar
18047  * 
18048  * Usage:
18049  *
18050  new Roo.bootstrap.HtmlEditor({
18051     ....
18052     toolbars : [
18053         new Roo.bootstrap.HtmlEditorToolbar1({
18054             disable : { fonts: 1 , format: 1, ..., ... , ...],
18055             btns : [ .... ]
18056         })
18057     }
18058      
18059  * 
18060  * @cfg {Object} disable List of elements to disable..
18061  * @cfg {Array} btns List of additional buttons.
18062  * 
18063  * 
18064  * NEEDS Extra CSS? 
18065  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
18066  */
18067  
18068 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
18069 {
18070     
18071     Roo.apply(this, config);
18072     
18073     // default disabled, based on 'good practice'..
18074     this.disable = this.disable || {};
18075     Roo.applyIf(this.disable, {
18076         fontSize : true,
18077         colors : true,
18078         specialElements : true
18079     });
18080     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
18081     
18082     this.editor = config.editor;
18083     this.editorcore = config.editor.editorcore;
18084     
18085     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
18086     
18087     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
18088     // dont call parent... till later.
18089 }
18090 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
18091      
18092     bar : true,
18093     
18094     editor : false,
18095     editorcore : false,
18096     
18097     
18098     formats : [
18099         "p" ,  
18100         "h1","h2","h3","h4","h5","h6", 
18101         "pre", "code", 
18102         "abbr", "acronym", "address", "cite", "samp", "var",
18103         'div','span'
18104     ],
18105     
18106     onRender : function(ct, position)
18107     {
18108        // Roo.log("Call onRender: " + this.xtype);
18109         
18110        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
18111        Roo.log(this.el);
18112        this.el.dom.style.marginBottom = '0';
18113        var _this = this;
18114        var editorcore = this.editorcore;
18115        var editor= this.editor;
18116        
18117        var children = [];
18118        var btn = function(id,cmd , toggle, handler){
18119        
18120             var  event = toggle ? 'toggle' : 'click';
18121        
18122             var a = {
18123                 size : 'sm',
18124                 xtype: 'Button',
18125                 xns: Roo.bootstrap,
18126                 glyphicon : id,
18127                 cmd : id || cmd,
18128                 enableToggle:toggle !== false,
18129                 //html : 'submit'
18130                 pressed : toggle ? false : null,
18131                 listeners : {}
18132             }
18133             a.listeners[toggle ? 'toggle' : 'click'] = function() {
18134                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
18135             }
18136             children.push(a);
18137             return a;
18138        }
18139         
18140         var style = {
18141                 xtype: 'Button',
18142                 size : 'sm',
18143                 xns: Roo.bootstrap,
18144                 glyphicon : 'font',
18145                 //html : 'submit'
18146                 menu : {
18147                     xtype: 'Menu',
18148                     xns: Roo.bootstrap,
18149                     items:  []
18150                 }
18151         };
18152         Roo.each(this.formats, function(f) {
18153             style.menu.items.push({
18154                 xtype :'MenuItem',
18155                 xns: Roo.bootstrap,
18156                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
18157                 tagname : f,
18158                 listeners : {
18159                     click : function()
18160                     {
18161                         editorcore.insertTag(this.tagname);
18162                         editor.focus();
18163                     }
18164                 }
18165                 
18166             });
18167         });
18168          children.push(style);   
18169             
18170             
18171         btn('bold',false,true);
18172         btn('italic',false,true);
18173         btn('align-left', 'justifyleft',true);
18174         btn('align-center', 'justifycenter',true);
18175         btn('align-right' , 'justifyright',true);
18176         btn('link', false, false, function(btn) {
18177             //Roo.log("create link?");
18178             var url = prompt(this.createLinkText, this.defaultLinkValue);
18179             if(url && url != 'http:/'+'/'){
18180                 this.editorcore.relayCmd('createlink', url);
18181             }
18182         }),
18183         btn('list','insertunorderedlist',true);
18184         btn('pencil', false,true, function(btn){
18185                 Roo.log(this);
18186                 
18187                 this.toggleSourceEdit(btn.pressed);
18188         });
18189         /*
18190         var cog = {
18191                 xtype: 'Button',
18192                 size : 'sm',
18193                 xns: Roo.bootstrap,
18194                 glyphicon : 'cog',
18195                 //html : 'submit'
18196                 menu : {
18197                     xtype: 'Menu',
18198                     xns: Roo.bootstrap,
18199                     items:  []
18200                 }
18201         };
18202         
18203         cog.menu.items.push({
18204             xtype :'MenuItem',
18205             xns: Roo.bootstrap,
18206             html : Clean styles,
18207             tagname : f,
18208             listeners : {
18209                 click : function()
18210                 {
18211                     editorcore.insertTag(this.tagname);
18212                     editor.focus();
18213                 }
18214             }
18215             
18216         });
18217        */
18218         
18219          
18220        this.xtype = 'NavSimplebar';
18221         
18222         for(var i=0;i< children.length;i++) {
18223             
18224             this.buttons.add(this.addxtypeChild(children[i]));
18225             
18226         }
18227         
18228         editor.on('editorevent', this.updateToolbar, this);
18229     },
18230     onBtnClick : function(id)
18231     {
18232        this.editorcore.relayCmd(id);
18233        this.editorcore.focus();
18234     },
18235     
18236     /**
18237      * Protected method that will not generally be called directly. It triggers
18238      * a toolbar update by reading the markup state of the current selection in the editor.
18239      */
18240     updateToolbar: function(){
18241
18242         if(!this.editorcore.activated){
18243             this.editor.onFirstFocus(); // is this neeed?
18244             return;
18245         }
18246
18247         var btns = this.buttons; 
18248         var doc = this.editorcore.doc;
18249         btns.get('bold').setActive(doc.queryCommandState('bold'));
18250         btns.get('italic').setActive(doc.queryCommandState('italic'));
18251         //btns.get('underline').setActive(doc.queryCommandState('underline'));
18252         
18253         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
18254         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
18255         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
18256         
18257         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
18258         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
18259          /*
18260         
18261         var ans = this.editorcore.getAllAncestors();
18262         if (this.formatCombo) {
18263             
18264             
18265             var store = this.formatCombo.store;
18266             this.formatCombo.setValue("");
18267             for (var i =0; i < ans.length;i++) {
18268                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
18269                     // select it..
18270                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
18271                     break;
18272                 }
18273             }
18274         }
18275         
18276         
18277         
18278         // hides menus... - so this cant be on a menu...
18279         Roo.bootstrap.MenuMgr.hideAll();
18280         */
18281         Roo.bootstrap.MenuMgr.hideAll();
18282         //this.editorsyncValue();
18283     },
18284     onFirstFocus: function() {
18285         this.buttons.each(function(item){
18286            item.enable();
18287         });
18288     },
18289     toggleSourceEdit : function(sourceEditMode){
18290         
18291           
18292         if(sourceEditMode){
18293             Roo.log("disabling buttons");
18294            this.buttons.each( function(item){
18295                 if(item.cmd != 'pencil'){
18296                     item.disable();
18297                 }
18298             });
18299           
18300         }else{
18301             Roo.log("enabling buttons");
18302             if(this.editorcore.initialized){
18303                 this.buttons.each( function(item){
18304                     item.enable();
18305                 });
18306             }
18307             
18308         }
18309         Roo.log("calling toggole on editor");
18310         // tell the editor that it's been pressed..
18311         this.editor.toggleSourceEdit(sourceEditMode);
18312        
18313     }
18314 });
18315
18316
18317
18318
18319
18320 /**
18321  * @class Roo.bootstrap.Table.AbstractSelectionModel
18322  * @extends Roo.util.Observable
18323  * Abstract base class for grid SelectionModels.  It provides the interface that should be
18324  * implemented by descendant classes.  This class should not be directly instantiated.
18325  * @constructor
18326  */
18327 Roo.bootstrap.Table.AbstractSelectionModel = function(){
18328     this.locked = false;
18329     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
18330 };
18331
18332
18333 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
18334     /** @ignore Called by the grid automatically. Do not call directly. */
18335     init : function(grid){
18336         this.grid = grid;
18337         this.initEvents();
18338     },
18339
18340     /**
18341      * Locks the selections.
18342      */
18343     lock : function(){
18344         this.locked = true;
18345     },
18346
18347     /**
18348      * Unlocks the selections.
18349      */
18350     unlock : function(){
18351         this.locked = false;
18352     },
18353
18354     /**
18355      * Returns true if the selections are locked.
18356      * @return {Boolean}
18357      */
18358     isLocked : function(){
18359         return this.locked;
18360     }
18361 });
18362 /**
18363  * @extends Roo.bootstrap.Table.AbstractSelectionModel
18364  * @class Roo.bootstrap.Table.RowSelectionModel
18365  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
18366  * It supports multiple selections and keyboard selection/navigation. 
18367  * @constructor
18368  * @param {Object} config
18369  */
18370
18371 Roo.bootstrap.Table.RowSelectionModel = function(config){
18372     Roo.apply(this, config);
18373     this.selections = new Roo.util.MixedCollection(false, function(o){
18374         return o.id;
18375     });
18376
18377     this.last = false;
18378     this.lastActive = false;
18379
18380     this.addEvents({
18381         /**
18382              * @event selectionchange
18383              * Fires when the selection changes
18384              * @param {SelectionModel} this
18385              */
18386             "selectionchange" : true,
18387         /**
18388              * @event afterselectionchange
18389              * Fires after the selection changes (eg. by key press or clicking)
18390              * @param {SelectionModel} this
18391              */
18392             "afterselectionchange" : true,
18393         /**
18394              * @event beforerowselect
18395              * Fires when a row is selected being selected, return false to cancel.
18396              * @param {SelectionModel} this
18397              * @param {Number} rowIndex The selected index
18398              * @param {Boolean} keepExisting False if other selections will be cleared
18399              */
18400             "beforerowselect" : true,
18401         /**
18402              * @event rowselect
18403              * Fires when a row is selected.
18404              * @param {SelectionModel} this
18405              * @param {Number} rowIndex The selected index
18406              * @param {Roo.data.Record} r The record
18407              */
18408             "rowselect" : true,
18409         /**
18410              * @event rowdeselect
18411              * Fires when a row is deselected.
18412              * @param {SelectionModel} this
18413              * @param {Number} rowIndex The selected index
18414              */
18415         "rowdeselect" : true
18416     });
18417     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
18418     this.locked = false;
18419 };
18420
18421 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
18422     /**
18423      * @cfg {Boolean} singleSelect
18424      * True to allow selection of only one row at a time (defaults to false)
18425      */
18426     singleSelect : false,
18427
18428     // private
18429     initEvents : function(){
18430
18431         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
18432             this.grid.on("mousedown", this.handleMouseDown, this);
18433         }else{ // allow click to work like normal
18434             this.grid.on("rowclick", this.handleDragableRowClick, this);
18435         }
18436
18437         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
18438             "up" : function(e){
18439                 if(!e.shiftKey){
18440                     this.selectPrevious(e.shiftKey);
18441                 }else if(this.last !== false && this.lastActive !== false){
18442                     var last = this.last;
18443                     this.selectRange(this.last,  this.lastActive-1);
18444                     this.grid.getView().focusRow(this.lastActive);
18445                     if(last !== false){
18446                         this.last = last;
18447                     }
18448                 }else{
18449                     this.selectFirstRow();
18450                 }
18451                 this.fireEvent("afterselectionchange", this);
18452             },
18453             "down" : function(e){
18454                 if(!e.shiftKey){
18455                     this.selectNext(e.shiftKey);
18456                 }else if(this.last !== false && this.lastActive !== false){
18457                     var last = this.last;
18458                     this.selectRange(this.last,  this.lastActive+1);
18459                     this.grid.getView().focusRow(this.lastActive);
18460                     if(last !== false){
18461                         this.last = last;
18462                     }
18463                 }else{
18464                     this.selectFirstRow();
18465                 }
18466                 this.fireEvent("afterselectionchange", this);
18467             },
18468             scope: this
18469         });
18470
18471         var view = this.grid.view;
18472         view.on("refresh", this.onRefresh, this);
18473         view.on("rowupdated", this.onRowUpdated, this);
18474         view.on("rowremoved", this.onRemove, this);
18475     },
18476
18477     // private
18478     onRefresh : function(){
18479         var ds = this.grid.dataSource, i, v = this.grid.view;
18480         var s = this.selections;
18481         s.each(function(r){
18482             if((i = ds.indexOfId(r.id)) != -1){
18483                 v.onRowSelect(i);
18484             }else{
18485                 s.remove(r);
18486             }
18487         });
18488     },
18489
18490     // private
18491     onRemove : function(v, index, r){
18492         this.selections.remove(r);
18493     },
18494
18495     // private
18496     onRowUpdated : function(v, index, r){
18497         if(this.isSelected(r)){
18498             v.onRowSelect(index);
18499         }
18500     },
18501
18502     /**
18503      * Select records.
18504      * @param {Array} records The records to select
18505      * @param {Boolean} keepExisting (optional) True to keep existing selections
18506      */
18507     selectRecords : function(records, keepExisting){
18508         if(!keepExisting){
18509             this.clearSelections();
18510         }
18511         var ds = this.grid.dataSource;
18512         for(var i = 0, len = records.length; i < len; i++){
18513             this.selectRow(ds.indexOf(records[i]), true);
18514         }
18515     },
18516
18517     /**
18518      * Gets the number of selected rows.
18519      * @return {Number}
18520      */
18521     getCount : function(){
18522         return this.selections.length;
18523     },
18524
18525     /**
18526      * Selects the first row in the grid.
18527      */
18528     selectFirstRow : function(){
18529         this.selectRow(0);
18530     },
18531
18532     /**
18533      * Select the last row.
18534      * @param {Boolean} keepExisting (optional) True to keep existing selections
18535      */
18536     selectLastRow : function(keepExisting){
18537         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
18538     },
18539
18540     /**
18541      * Selects the row immediately following the last selected row.
18542      * @param {Boolean} keepExisting (optional) True to keep existing selections
18543      */
18544     selectNext : function(keepExisting){
18545         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
18546             this.selectRow(this.last+1, keepExisting);
18547             this.grid.getView().focusRow(this.last);
18548         }
18549     },
18550
18551     /**
18552      * Selects the row that precedes the last selected row.
18553      * @param {Boolean} keepExisting (optional) True to keep existing selections
18554      */
18555     selectPrevious : function(keepExisting){
18556         if(this.last){
18557             this.selectRow(this.last-1, keepExisting);
18558             this.grid.getView().focusRow(this.last);
18559         }
18560     },
18561
18562     /**
18563      * Returns the selected records
18564      * @return {Array} Array of selected records
18565      */
18566     getSelections : function(){
18567         return [].concat(this.selections.items);
18568     },
18569
18570     /**
18571      * Returns the first selected record.
18572      * @return {Record}
18573      */
18574     getSelected : function(){
18575         return this.selections.itemAt(0);
18576     },
18577
18578
18579     /**
18580      * Clears all selections.
18581      */
18582     clearSelections : function(fast){
18583         if(this.locked) return;
18584         if(fast !== true){
18585             var ds = this.grid.dataSource;
18586             var s = this.selections;
18587             s.each(function(r){
18588                 this.deselectRow(ds.indexOfId(r.id));
18589             }, this);
18590             s.clear();
18591         }else{
18592             this.selections.clear();
18593         }
18594         this.last = false;
18595     },
18596
18597
18598     /**
18599      * Selects all rows.
18600      */
18601     selectAll : function(){
18602         if(this.locked) return;
18603         this.selections.clear();
18604         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
18605             this.selectRow(i, true);
18606         }
18607     },
18608
18609     /**
18610      * Returns True if there is a selection.
18611      * @return {Boolean}
18612      */
18613     hasSelection : function(){
18614         return this.selections.length > 0;
18615     },
18616
18617     /**
18618      * Returns True if the specified row is selected.
18619      * @param {Number/Record} record The record or index of the record to check
18620      * @return {Boolean}
18621      */
18622     isSelected : function(index){
18623         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
18624         return (r && this.selections.key(r.id) ? true : false);
18625     },
18626
18627     /**
18628      * Returns True if the specified record id is selected.
18629      * @param {String} id The id of record to check
18630      * @return {Boolean}
18631      */
18632     isIdSelected : function(id){
18633         return (this.selections.key(id) ? true : false);
18634     },
18635
18636     // private
18637     handleMouseDown : function(e, t){
18638         var view = this.grid.getView(), rowIndex;
18639         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
18640             return;
18641         };
18642         if(e.shiftKey && this.last !== false){
18643             var last = this.last;
18644             this.selectRange(last, rowIndex, e.ctrlKey);
18645             this.last = last; // reset the last
18646             view.focusRow(rowIndex);
18647         }else{
18648             var isSelected = this.isSelected(rowIndex);
18649             if(e.button !== 0 && isSelected){
18650                 view.focusRow(rowIndex);
18651             }else if(e.ctrlKey && isSelected){
18652                 this.deselectRow(rowIndex);
18653             }else if(!isSelected){
18654                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
18655                 view.focusRow(rowIndex);
18656             }
18657         }
18658         this.fireEvent("afterselectionchange", this);
18659     },
18660     // private
18661     handleDragableRowClick :  function(grid, rowIndex, e) 
18662     {
18663         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
18664             this.selectRow(rowIndex, false);
18665             grid.view.focusRow(rowIndex);
18666              this.fireEvent("afterselectionchange", this);
18667         }
18668     },
18669     
18670     /**
18671      * Selects multiple rows.
18672      * @param {Array} rows Array of the indexes of the row to select
18673      * @param {Boolean} keepExisting (optional) True to keep existing selections
18674      */
18675     selectRows : function(rows, keepExisting){
18676         if(!keepExisting){
18677             this.clearSelections();
18678         }
18679         for(var i = 0, len = rows.length; i < len; i++){
18680             this.selectRow(rows[i], true);
18681         }
18682     },
18683
18684     /**
18685      * Selects a range of rows. All rows in between startRow and endRow are also selected.
18686      * @param {Number} startRow The index of the first row in the range
18687      * @param {Number} endRow The index of the last row in the range
18688      * @param {Boolean} keepExisting (optional) True to retain existing selections
18689      */
18690     selectRange : function(startRow, endRow, keepExisting){
18691         if(this.locked) return;
18692         if(!keepExisting){
18693             this.clearSelections();
18694         }
18695         if(startRow <= endRow){
18696             for(var i = startRow; i <= endRow; i++){
18697                 this.selectRow(i, true);
18698             }
18699         }else{
18700             for(var i = startRow; i >= endRow; i--){
18701                 this.selectRow(i, true);
18702             }
18703         }
18704     },
18705
18706     /**
18707      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
18708      * @param {Number} startRow The index of the first row in the range
18709      * @param {Number} endRow The index of the last row in the range
18710      */
18711     deselectRange : function(startRow, endRow, preventViewNotify){
18712         if(this.locked) return;
18713         for(var i = startRow; i <= endRow; i++){
18714             this.deselectRow(i, preventViewNotify);
18715         }
18716     },
18717
18718     /**
18719      * Selects a row.
18720      * @param {Number} row The index of the row to select
18721      * @param {Boolean} keepExisting (optional) True to keep existing selections
18722      */
18723     selectRow : function(index, keepExisting, preventViewNotify){
18724         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
18725         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
18726             if(!keepExisting || this.singleSelect){
18727                 this.clearSelections();
18728             }
18729             var r = this.grid.dataSource.getAt(index);
18730             this.selections.add(r);
18731             this.last = this.lastActive = index;
18732             if(!preventViewNotify){
18733                 this.grid.getView().onRowSelect(index);
18734             }
18735             this.fireEvent("rowselect", this, index, r);
18736             this.fireEvent("selectionchange", this);
18737         }
18738     },
18739
18740     /**
18741      * Deselects a row.
18742      * @param {Number} row The index of the row to deselect
18743      */
18744     deselectRow : function(index, preventViewNotify){
18745         if(this.locked) return;
18746         if(this.last == index){
18747             this.last = false;
18748         }
18749         if(this.lastActive == index){
18750             this.lastActive = false;
18751         }
18752         var r = this.grid.dataSource.getAt(index);
18753         this.selections.remove(r);
18754         if(!preventViewNotify){
18755             this.grid.getView().onRowDeselect(index);
18756         }
18757         this.fireEvent("rowdeselect", this, index);
18758         this.fireEvent("selectionchange", this);
18759     },
18760
18761     // private
18762     restoreLast : function(){
18763         if(this._last){
18764             this.last = this._last;
18765         }
18766     },
18767
18768     // private
18769     acceptsNav : function(row, col, cm){
18770         return !cm.isHidden(col) && cm.isCellEditable(col, row);
18771     },
18772
18773     // private
18774     onEditorKey : function(field, e){
18775         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
18776         if(k == e.TAB){
18777             e.stopEvent();
18778             ed.completeEdit();
18779             if(e.shiftKey){
18780                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
18781             }else{
18782                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
18783             }
18784         }else if(k == e.ENTER && !e.ctrlKey){
18785             e.stopEvent();
18786             ed.completeEdit();
18787             if(e.shiftKey){
18788                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
18789             }else{
18790                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
18791             }
18792         }else if(k == e.ESC){
18793             ed.cancelEdit();
18794         }
18795         if(newCell){
18796             g.startEditing(newCell[0], newCell[1]);
18797         }
18798     }
18799 });/*
18800  * Based on:
18801  * Ext JS Library 1.1.1
18802  * Copyright(c) 2006-2007, Ext JS, LLC.
18803  *
18804  * Originally Released Under LGPL - original licence link has changed is not relivant.
18805  *
18806  * Fork - LGPL
18807  * <script type="text/javascript">
18808  */
18809  
18810 /**
18811  * @class Roo.bootstrap.PagingToolbar
18812  * @extends Roo.Row
18813  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
18814  * @constructor
18815  * Create a new PagingToolbar
18816  * @param {Object} config The config object
18817  */
18818 Roo.bootstrap.PagingToolbar = function(config)
18819 {
18820     // old args format still supported... - xtype is prefered..
18821         // created from xtype...
18822     var ds = config.dataSource;
18823     this.toolbarItems = [];
18824     if (config.items) {
18825         this.toolbarItems = config.items;
18826 //        config.items = [];
18827     }
18828     
18829     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
18830     this.ds = ds;
18831     this.cursor = 0;
18832     if (ds) { 
18833         this.bind(ds);
18834     }
18835     
18836     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
18837     
18838 };
18839
18840 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
18841     /**
18842      * @cfg {Roo.data.Store} dataSource
18843      * The underlying data store providing the paged data
18844      */
18845     /**
18846      * @cfg {String/HTMLElement/Element} container
18847      * container The id or element that will contain the toolbar
18848      */
18849     /**
18850      * @cfg {Boolean} displayInfo
18851      * True to display the displayMsg (defaults to false)
18852      */
18853     /**
18854      * @cfg {Number} pageSize
18855      * The number of records to display per page (defaults to 20)
18856      */
18857     pageSize: 20,
18858     /**
18859      * @cfg {String} displayMsg
18860      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
18861      */
18862     displayMsg : 'Displaying {0} - {1} of {2}',
18863     /**
18864      * @cfg {String} emptyMsg
18865      * The message to display when no records are found (defaults to "No data to display")
18866      */
18867     emptyMsg : 'No data to display',
18868     /**
18869      * Customizable piece of the default paging text (defaults to "Page")
18870      * @type String
18871      */
18872     beforePageText : "Page",
18873     /**
18874      * Customizable piece of the default paging text (defaults to "of %0")
18875      * @type String
18876      */
18877     afterPageText : "of {0}",
18878     /**
18879      * Customizable piece of the default paging text (defaults to "First Page")
18880      * @type String
18881      */
18882     firstText : "First Page",
18883     /**
18884      * Customizable piece of the default paging text (defaults to "Previous Page")
18885      * @type String
18886      */
18887     prevText : "Previous Page",
18888     /**
18889      * Customizable piece of the default paging text (defaults to "Next Page")
18890      * @type String
18891      */
18892     nextText : "Next Page",
18893     /**
18894      * Customizable piece of the default paging text (defaults to "Last Page")
18895      * @type String
18896      */
18897     lastText : "Last Page",
18898     /**
18899      * Customizable piece of the default paging text (defaults to "Refresh")
18900      * @type String
18901      */
18902     refreshText : "Refresh",
18903
18904     buttons : false,
18905     // private
18906     onRender : function(ct, position) 
18907     {
18908         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
18909         this.navgroup.parentId = this.id;
18910         this.navgroup.onRender(this.el, null);
18911         // add the buttons to the navgroup
18912         
18913         if(this.displayInfo){
18914             Roo.log(this.el.select('ul.navbar-nav',true).first());
18915             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
18916             this.displayEl = this.el.select('.x-paging-info', true).first();
18917 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
18918 //            this.displayEl = navel.el.select('span',true).first();
18919         }
18920         
18921         var _this = this;
18922         
18923         if(this.buttons){
18924             Roo.each(_this.buttons, function(e){
18925                Roo.factory(e).onRender(_this.el, null);
18926             });
18927         }
18928             
18929         Roo.each(_this.toolbarItems, function(e) {
18930             _this.navgroup.addItem(e);
18931         });
18932         
18933         this.first = this.navgroup.addItem({
18934             tooltip: this.firstText,
18935             cls: "prev",
18936             icon : 'fa fa-backward',
18937             disabled: true,
18938             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
18939         });
18940         
18941         this.prev =  this.navgroup.addItem({
18942             tooltip: this.prevText,
18943             cls: "prev",
18944             icon : 'fa fa-step-backward',
18945             disabled: true,
18946             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
18947         });
18948     //this.addSeparator();
18949         
18950         
18951         var field = this.navgroup.addItem( {
18952             tagtype : 'span',
18953             cls : 'x-paging-position',
18954             
18955             html : this.beforePageText  +
18956                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
18957                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
18958          } ); //?? escaped?
18959         
18960         this.field = field.el.select('input', true).first();
18961         this.field.on("keydown", this.onPagingKeydown, this);
18962         this.field.on("focus", function(){this.dom.select();});
18963     
18964     
18965         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
18966         //this.field.setHeight(18);
18967         //this.addSeparator();
18968         this.next = this.navgroup.addItem({
18969             tooltip: this.nextText,
18970             cls: "next",
18971             html : ' <i class="fa fa-step-forward">',
18972             disabled: true,
18973             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
18974         });
18975         this.last = this.navgroup.addItem({
18976             tooltip: this.lastText,
18977             icon : 'fa fa-forward',
18978             cls: "next",
18979             disabled: true,
18980             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
18981         });
18982     //this.addSeparator();
18983         this.loading = this.navgroup.addItem({
18984             tooltip: this.refreshText,
18985             icon: 'fa fa-refresh',
18986             
18987             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
18988         });
18989
18990     },
18991
18992     // private
18993     updateInfo : function(){
18994         if(this.displayEl){
18995             var count = this.ds.getCount();
18996             var msg = count == 0 ?
18997                 this.emptyMsg :
18998                 String.format(
18999                     this.displayMsg,
19000                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
19001                 );
19002             this.displayEl.update(msg);
19003         }
19004     },
19005
19006     // private
19007     onLoad : function(ds, r, o){
19008        this.cursor = o.params ? o.params.start : 0;
19009        var d = this.getPageData(),
19010             ap = d.activePage,
19011             ps = d.pages;
19012         
19013        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
19014        this.field.dom.value = ap;
19015        this.first.setDisabled(ap == 1);
19016        this.prev.setDisabled(ap == 1);
19017        this.next.setDisabled(ap == ps);
19018        this.last.setDisabled(ap == ps);
19019        this.loading.enable();
19020        this.updateInfo();
19021     },
19022
19023     // private
19024     getPageData : function(){
19025         var total = this.ds.getTotalCount();
19026         return {
19027             total : total,
19028             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
19029             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
19030         };
19031     },
19032
19033     // private
19034     onLoadError : function(){
19035         this.loading.enable();
19036     },
19037
19038     // private
19039     onPagingKeydown : function(e){
19040         var k = e.getKey();
19041         var d = this.getPageData();
19042         if(k == e.RETURN){
19043             var v = this.field.dom.value, pageNum;
19044             if(!v || isNaN(pageNum = parseInt(v, 10))){
19045                 this.field.dom.value = d.activePage;
19046                 return;
19047             }
19048             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
19049             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
19050             e.stopEvent();
19051         }
19052         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))
19053         {
19054           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
19055           this.field.dom.value = pageNum;
19056           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
19057           e.stopEvent();
19058         }
19059         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
19060         {
19061           var v = this.field.dom.value, pageNum; 
19062           var increment = (e.shiftKey) ? 10 : 1;
19063           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
19064             increment *= -1;
19065           if(!v || isNaN(pageNum = parseInt(v, 10))) {
19066             this.field.dom.value = d.activePage;
19067             return;
19068           }
19069           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
19070           {
19071             this.field.dom.value = parseInt(v, 10) + increment;
19072             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
19073             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
19074           }
19075           e.stopEvent();
19076         }
19077     },
19078
19079     // private
19080     beforeLoad : function(){
19081         if(this.loading){
19082             this.loading.disable();
19083         }
19084     },
19085
19086     // private
19087     onClick : function(which){
19088         var ds = this.ds;
19089         if (!ds) {
19090             return;
19091         }
19092         switch(which){
19093             case "first":
19094                 ds.load({params:{start: 0, limit: this.pageSize}});
19095             break;
19096             case "prev":
19097                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
19098             break;
19099             case "next":
19100                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
19101             break;
19102             case "last":
19103                 var total = ds.getTotalCount();
19104                 var extra = total % this.pageSize;
19105                 var lastStart = extra ? (total - extra) : total-this.pageSize;
19106                 ds.load({params:{start: lastStart, limit: this.pageSize}});
19107             break;
19108             case "refresh":
19109                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
19110             break;
19111         }
19112     },
19113
19114     /**
19115      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
19116      * @param {Roo.data.Store} store The data store to unbind
19117      */
19118     unbind : function(ds){
19119         ds.un("beforeload", this.beforeLoad, this);
19120         ds.un("load", this.onLoad, this);
19121         ds.un("loadexception", this.onLoadError, this);
19122         ds.un("remove", this.updateInfo, this);
19123         ds.un("add", this.updateInfo, this);
19124         this.ds = undefined;
19125     },
19126
19127     /**
19128      * Binds the paging toolbar to the specified {@link Roo.data.Store}
19129      * @param {Roo.data.Store} store The data store to bind
19130      */
19131     bind : function(ds){
19132         ds.on("beforeload", this.beforeLoad, this);
19133         ds.on("load", this.onLoad, this);
19134         ds.on("loadexception", this.onLoadError, this);
19135         ds.on("remove", this.updateInfo, this);
19136         ds.on("add", this.updateInfo, this);
19137         this.ds = ds;
19138     }
19139 });/*
19140  * - LGPL
19141  *
19142  * element
19143  * 
19144  */
19145
19146 /**
19147  * @class Roo.bootstrap.MessageBar
19148  * @extends Roo.bootstrap.Component
19149  * Bootstrap MessageBar class
19150  * @cfg {String} html contents of the MessageBar
19151  * @cfg {String} weight (info | success | warning | danger) default info
19152  * @cfg {String} beforeClass insert the bar before the given class
19153  * @cfg {Boolean} closable (true | false) default false
19154  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
19155  * 
19156  * @constructor
19157  * Create a new Element
19158  * @param {Object} config The config object
19159  */
19160
19161 Roo.bootstrap.MessageBar = function(config){
19162     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
19163 };
19164
19165 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
19166     
19167     html: '',
19168     weight: 'info',
19169     closable: false,
19170     fixed: false,
19171     beforeClass: 'bootstrap-sticky-wrap',
19172     
19173     getAutoCreate : function(){
19174         
19175         var cfg = {
19176             tag: 'div',
19177             cls: 'alert alert-dismissable alert-' + this.weight,
19178             cn: [
19179                 {
19180                     tag: 'span',
19181                     cls: 'message',
19182                     html: this.html || ''
19183                 }
19184             ]
19185         }
19186         
19187         if(this.fixed){
19188             cfg.cls += ' alert-messages-fixed';
19189         }
19190         
19191         if(this.closable){
19192             cfg.cn.push({
19193                 tag: 'button',
19194                 cls: 'close',
19195                 html: 'x'
19196             });
19197         }
19198         
19199         return cfg;
19200     },
19201     
19202     onRender : function(ct, position)
19203     {
19204         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19205         
19206         if(!this.el){
19207             var cfg = Roo.apply({},  this.getAutoCreate());
19208             cfg.id = Roo.id();
19209             
19210             if (this.cls) {
19211                 cfg.cls += ' ' + this.cls;
19212             }
19213             if (this.style) {
19214                 cfg.style = this.style;
19215             }
19216             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
19217             
19218             this.el.setVisibilityMode(Roo.Element.DISPLAY);
19219         }
19220         
19221         this.el.select('>button.close').on('click', this.hide, this);
19222         
19223     },
19224     
19225     show : function()
19226     {
19227         if (!this.rendered) {
19228             this.render();
19229         }
19230         
19231         this.el.show();
19232         
19233         this.fireEvent('show', this);
19234         
19235     },
19236     
19237     hide : function()
19238     {
19239         if (!this.rendered) {
19240             this.render();
19241         }
19242         
19243         this.el.hide();
19244         
19245         this.fireEvent('hide', this);
19246     },
19247     
19248     update : function()
19249     {
19250 //        var e = this.el.dom.firstChild;
19251 //        
19252 //        if(this.closable){
19253 //            e = e.nextSibling;
19254 //        }
19255 //        
19256 //        e.data = this.html || '';
19257
19258         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
19259     }
19260    
19261 });
19262
19263  
19264
19265      /*
19266  * - LGPL
19267  *
19268  * Graph
19269  * 
19270  */
19271
19272
19273 /**
19274  * @class Roo.bootstrap.Graph
19275  * @extends Roo.bootstrap.Component
19276  * Bootstrap Graph class
19277 > Prameters
19278  -sm {number} sm 4
19279  -md {number} md 5
19280  @cfg {String} graphtype  bar | vbar | pie
19281  @cfg {number} g_x coodinator | centre x (pie)
19282  @cfg {number} g_y coodinator | centre y (pie)
19283  @cfg {number} g_r radius (pie)
19284  @cfg {number} g_height height of the chart (respected by all elements in the set)
19285  @cfg {number} g_width width of the chart (respected by all elements in the set)
19286  @cfg {Object} title The title of the chart
19287     
19288  -{Array}  values
19289  -opts (object) options for the chart 
19290      o {
19291      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
19292      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
19293      o vgutter (number)
19294      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.
19295      o stacked (boolean) whether or not to tread values as in a stacked bar chart
19296      o to
19297      o stretch (boolean)
19298      o }
19299  -opts (object) options for the pie
19300      o{
19301      o cut
19302      o startAngle (number)
19303      o endAngle (number)
19304      } 
19305  *
19306  * @constructor
19307  * Create a new Input
19308  * @param {Object} config The config object
19309  */
19310
19311 Roo.bootstrap.Graph = function(config){
19312     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
19313     
19314     this.addEvents({
19315         // img events
19316         /**
19317          * @event click
19318          * The img click event for the img.
19319          * @param {Roo.EventObject} e
19320          */
19321         "click" : true
19322     });
19323 };
19324
19325 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
19326     
19327     sm: 4,
19328     md: 5,
19329     graphtype: 'bar',
19330     g_height: 250,
19331     g_width: 400,
19332     g_x: 50,
19333     g_y: 50,
19334     g_r: 30,
19335     opts:{
19336         //g_colors: this.colors,
19337         g_type: 'soft',
19338         g_gutter: '20%'
19339
19340     },
19341     title : false,
19342
19343     getAutoCreate : function(){
19344         
19345         var cfg = {
19346             tag: 'div',
19347             html : null
19348         }
19349         
19350         
19351         return  cfg;
19352     },
19353
19354     onRender : function(ct,position){
19355         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
19356         this.raphael = Raphael(this.el.dom);
19357         
19358                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19359                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19360                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19361                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
19362                 /*
19363                 r.text(160, 10, "Single Series Chart").attr(txtattr);
19364                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
19365                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
19366                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
19367                 
19368                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
19369                 r.barchart(330, 10, 300, 220, data1);
19370                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
19371                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
19372                 */
19373                 
19374                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19375                 // r.barchart(30, 30, 560, 250,  xdata, {
19376                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
19377                 //     axis : "0 0 1 1",
19378                 //     axisxlabels :  xdata
19379                 //     //yvalues : cols,
19380                    
19381                 // });
19382 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19383 //        
19384 //        this.load(null,xdata,{
19385 //                axis : "0 0 1 1",
19386 //                axisxlabels :  xdata
19387 //                });
19388
19389     },
19390
19391     load : function(graphtype,xdata,opts){
19392         this.raphael.clear();
19393         if(!graphtype) {
19394             graphtype = this.graphtype;
19395         }
19396         if(!opts){
19397             opts = this.opts;
19398         }
19399         var r = this.raphael,
19400             fin = function () {
19401                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
19402             },
19403             fout = function () {
19404                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
19405             },
19406             pfin = function() {
19407                 this.sector.stop();
19408                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
19409
19410                 if (this.label) {
19411                     this.label[0].stop();
19412                     this.label[0].attr({ r: 7.5 });
19413                     this.label[1].attr({ "font-weight": 800 });
19414                 }
19415             },
19416             pfout = function() {
19417                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
19418
19419                 if (this.label) {
19420                     this.label[0].animate({ r: 5 }, 500, "bounce");
19421                     this.label[1].attr({ "font-weight": 400 });
19422                 }
19423             };
19424
19425         switch(graphtype){
19426             case 'bar':
19427                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19428                 break;
19429             case 'hbar':
19430                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19431                 break;
19432             case 'pie':
19433 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
19434 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
19435 //            
19436                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
19437                 
19438                 break;
19439
19440         }
19441         
19442         if(this.title){
19443             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
19444         }
19445         
19446     },
19447     
19448     setTitle: function(o)
19449     {
19450         this.title = o;
19451     },
19452     
19453     initEvents: function() {
19454         
19455         if(!this.href){
19456             this.el.on('click', this.onClick, this);
19457         }
19458     },
19459     
19460     onClick : function(e)
19461     {
19462         Roo.log('img onclick');
19463         this.fireEvent('click', this, e);
19464     }
19465    
19466 });
19467
19468  
19469 /*
19470  * - LGPL
19471  *
19472  * numberBox
19473  * 
19474  */
19475 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19476
19477 /**
19478  * @class Roo.bootstrap.dash.NumberBox
19479  * @extends Roo.bootstrap.Component
19480  * Bootstrap NumberBox class
19481  * @cfg {String} bgcolor Box background color, such as (aqua | green | yellow | red etc..) Default aqua
19482  * @cfg {String} headline Box headline
19483  * @cfg {String} content Box content
19484  * @cfg {String} icon Box icon
19485  * @cfg {String} footer Footer text
19486  * @cfg {String} fhref Footer href
19487  * 
19488  * @constructor
19489  * Create a new NumberBox
19490  * @param {Object} config The config object
19491  */
19492
19493
19494 Roo.bootstrap.dash.NumberBox = function(config){
19495     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
19496     
19497 };
19498
19499 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
19500     
19501     bgcolor : 'aqua',
19502     headline : '',
19503     content : '',
19504     icon : '',
19505     footer : '',
19506     fhref : '',
19507     ficon : '',
19508     
19509     getAutoCreate : function(){
19510         
19511         var cfg = {
19512             tag : 'div',
19513             cls : 'small-box bg-' + this.bgcolor,
19514             cn : [
19515                 {
19516                     tag : 'div',
19517                     cls : 'inner',
19518                     cn :[
19519                         {
19520                             tag : 'h3',
19521                             cls : 'roo-headline',
19522                             html : this.headline
19523                         },
19524                         {
19525                             tag : 'p',
19526                             cls : 'roo-content',
19527                             html : this.content
19528                         }
19529                     ]
19530                 }
19531             ]
19532         }
19533         
19534         if(this.icon){
19535             cfg.cn.push({
19536                 tag : 'div',
19537                 cls : 'icon',
19538                 cn :[
19539                     {
19540                         tag : 'i',
19541                         cls : 'ion ' + this.icon
19542                     }
19543                 ]
19544             });
19545         }
19546         
19547         if(this.footer){
19548             var footer = {
19549                 tag : 'a',
19550                 cls : 'small-box-footer',
19551                 href : this.fhref || '#',
19552                 html : this.footer
19553             };
19554             
19555             cfg.cn.push(footer);
19556             
19557         }
19558         
19559         return  cfg;
19560     },
19561
19562     onRender : function(ct,position){
19563         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
19564
19565
19566        
19567                 
19568     },
19569
19570     setHeadline: function (value)
19571     {
19572         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
19573     },
19574     
19575     setFooter: function (value, href)
19576     {
19577         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
19578         
19579         if(href){
19580             this.el.select('a.small-box-footer',true).first().attr('href', href);
19581         }
19582         
19583     },
19584
19585     setContent: function (value)
19586     {
19587         this.el.select('.roo-content',true).first().dom.innerHTML = value;
19588     },
19589
19590     initEvents: function() 
19591     {   
19592         
19593     }
19594     
19595 });
19596
19597  
19598 /*
19599  * - LGPL
19600  *
19601  * TabBox
19602  * 
19603  */
19604 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19605
19606 /**
19607  * @class Roo.bootstrap.dash.TabBox
19608  * @extends Roo.bootstrap.Component
19609  * Bootstrap TabBox class
19610  * @cfg {String} title Title of the TabBox
19611  * @cfg {String} icon Icon of the TabBox
19612  * @cfg {Boolean} showtabs (true|false) show the tabs default true
19613  * 
19614  * @constructor
19615  * Create a new TabBox
19616  * @param {Object} config The config object
19617  */
19618
19619
19620 Roo.bootstrap.dash.TabBox = function(config){
19621     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
19622     this.addEvents({
19623         // raw events
19624         /**
19625          * @event addpane
19626          * When a pane is added
19627          * @param {Roo.bootstrap.dash.TabPane} pane
19628          */
19629         "addpane" : true
19630          
19631     });
19632 };
19633
19634 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
19635
19636     title : '',
19637     icon : false,
19638     showtabs : true,
19639     
19640     getChildContainer : function()
19641     {
19642         return this.el.select('.tab-content', true).first();
19643     },
19644     
19645     getAutoCreate : function(){
19646         
19647         var header = {
19648             tag: 'li',
19649             cls: 'pull-left header',
19650             html: this.title,
19651             cn : []
19652         };
19653         
19654         if(this.icon){
19655             header.cn.push({
19656                 tag: 'i',
19657                 cls: 'fa ' + this.icon
19658             });
19659         }
19660         
19661         
19662         var cfg = {
19663             tag: 'div',
19664             cls: 'nav-tabs-custom',
19665             cn: [
19666                 {
19667                     tag: 'ul',
19668                     cls: 'nav nav-tabs pull-right',
19669                     cn: [
19670                         header
19671                     ]
19672                 },
19673                 {
19674                     tag: 'div',
19675                     cls: 'tab-content no-padding',
19676                     cn: []
19677                 }
19678             ]
19679         }
19680
19681         return  cfg;
19682     },
19683     initEvents : function()
19684     {
19685         //Roo.log('add add pane handler');
19686         this.on('addpane', this.onAddPane, this);
19687     },
19688      /**
19689      * Updates the box title
19690      * @param {String} html to set the title to.
19691      */
19692     setTitle : function(value)
19693     {
19694         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
19695     },
19696     onAddPane : function(pane)
19697     {
19698         //Roo.log('addpane');
19699         //Roo.log(pane);
19700         // tabs are rendere left to right..
19701         if(!this.showtabs){
19702             return;
19703         }
19704         
19705         var ctr = this.el.select('.nav-tabs', true).first();
19706          
19707          
19708         var existing = ctr.select('.nav-tab',true);
19709         var qty = existing.getCount();;
19710         
19711         
19712         var tab = ctr.createChild({
19713             tag : 'li',
19714             cls : 'nav-tab' + (qty ? '' : ' active'),
19715             cn : [
19716                 {
19717                     tag : 'a',
19718                     href:'#',
19719                     html : pane.title
19720                 }
19721             ]
19722         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
19723         pane.tab = tab;
19724         
19725         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
19726         if (!qty) {
19727             pane.el.addClass('active');
19728         }
19729         
19730                 
19731     },
19732     onTabClick : function(ev,un,ob,pane)
19733     {
19734         //Roo.log('tab - prev default');
19735         ev.preventDefault();
19736         
19737         
19738         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
19739         pane.tab.addClass('active');
19740         //Roo.log(pane.title);
19741         this.getChildContainer().select('.tab-pane',true).removeClass('active');
19742         // technically we should have a deactivate event.. but maybe add later.
19743         // and it should not de-activate the selected tab...
19744         
19745         pane.el.addClass('active');
19746         pane.fireEvent('activate');
19747         
19748         
19749     }
19750     
19751     
19752 });
19753
19754  
19755 /*
19756  * - LGPL
19757  *
19758  * Tab pane
19759  * 
19760  */
19761 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19762 /**
19763  * @class Roo.bootstrap.TabPane
19764  * @extends Roo.bootstrap.Component
19765  * Bootstrap TabPane class
19766  * @cfg {Boolean} active (false | true) Default false
19767  * @cfg {String} title title of panel
19768
19769  * 
19770  * @constructor
19771  * Create a new TabPane
19772  * @param {Object} config The config object
19773  */
19774
19775 Roo.bootstrap.dash.TabPane = function(config){
19776     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
19777     
19778     this.addEvents({
19779         // raw events
19780         /**
19781          * @event activate
19782          * When a pane is activated
19783          * @param {Roo.bootstrap.dash.TabPane} pane
19784          */
19785         "activate" : true
19786          
19787     });
19788 };
19789
19790 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
19791     
19792     active : false,
19793     title : '',
19794     
19795     // the tabBox that this is attached to.
19796     tab : false,
19797      
19798     getAutoCreate : function() 
19799     {
19800         var cfg = {
19801             tag: 'div',
19802             cls: 'tab-pane'
19803         }
19804         
19805         if(this.active){
19806             cfg.cls += ' active';
19807         }
19808         
19809         return cfg;
19810     },
19811     initEvents  : function()
19812     {
19813         //Roo.log('trigger add pane handler');
19814         this.parent().fireEvent('addpane', this)
19815     },
19816     
19817      /**
19818      * Updates the tab title 
19819      * @param {String} html to set the title to.
19820      */
19821     setTitle: function(str)
19822     {
19823         if (!this.tab) {
19824             return;
19825         }
19826         this.title = str;
19827         this.tab.select('a', true).first().dom.innerHTML = str;
19828         
19829     }
19830     
19831     
19832     
19833 });
19834
19835  
19836
19837
19838  /*
19839  * - LGPL
19840  *
19841  * menu
19842  * 
19843  */
19844 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
19845
19846 /**
19847  * @class Roo.bootstrap.menu.Menu
19848  * @extends Roo.bootstrap.Component
19849  * Bootstrap Menu class - container for Menu
19850  * @cfg {String} html Text of the menu
19851  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
19852  * @cfg {String} icon Font awesome icon
19853  * @cfg {String} pos Menu align to (top | bottom) default bottom
19854  * 
19855  * 
19856  * @constructor
19857  * Create a new Menu
19858  * @param {Object} config The config object
19859  */
19860
19861
19862 Roo.bootstrap.menu.Menu = function(config){
19863     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
19864     
19865     this.addEvents({
19866         /**
19867          * @event beforeshow
19868          * Fires before this menu is displayed
19869          * @param {Roo.bootstrap.menu.Menu} this
19870          */
19871         beforeshow : true,
19872         /**
19873          * @event beforehide
19874          * Fires before this menu is hidden
19875          * @param {Roo.bootstrap.menu.Menu} this
19876          */
19877         beforehide : true,
19878         /**
19879          * @event show
19880          * Fires after this menu is displayed
19881          * @param {Roo.bootstrap.menu.Menu} this
19882          */
19883         show : true,
19884         /**
19885          * @event hide
19886          * Fires after this menu is hidden
19887          * @param {Roo.bootstrap.menu.Menu} this
19888          */
19889         hide : true,
19890         /**
19891          * @event click
19892          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19893          * @param {Roo.bootstrap.menu.Menu} this
19894          * @param {Roo.EventObject} e
19895          */
19896         click : true
19897     });
19898     
19899 };
19900
19901 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
19902     
19903     submenu : false,
19904     html : '',
19905     weight : 'default',
19906     icon : false,
19907     pos : 'bottom',
19908     
19909     
19910     getChildContainer : function() {
19911         if(this.isSubMenu){
19912             return this.el;
19913         }
19914         
19915         return this.el.select('ul.dropdown-menu', true).first();  
19916     },
19917     
19918     getAutoCreate : function()
19919     {
19920         var text = [
19921             {
19922                 tag : 'span',
19923                 cls : 'roo-menu-text',
19924                 html : this.html
19925             }
19926         ];
19927         
19928         if(this.icon){
19929             text.unshift({
19930                 tag : 'i',
19931                 cls : 'fa ' + this.icon
19932             })
19933         }
19934         
19935         
19936         var cfg = {
19937             tag : 'div',
19938             cls : 'btn-group',
19939             cn : [
19940                 {
19941                     tag : 'button',
19942                     cls : 'dropdown-button btn btn-' + this.weight,
19943                     cn : text
19944                 },
19945                 {
19946                     tag : 'button',
19947                     cls : 'dropdown-toggle btn btn-' + this.weight,
19948                     cn : [
19949                         {
19950                             tag : 'span',
19951                             cls : 'caret'
19952                         }
19953                     ]
19954                 },
19955                 {
19956                     tag : 'ul',
19957                     cls : 'dropdown-menu'
19958                 }
19959             ]
19960             
19961         };
19962         
19963         if(this.pos == 'top'){
19964             cfg.cls += ' dropup';
19965         }
19966         
19967         if(this.isSubMenu){
19968             cfg = {
19969                 tag : 'ul',
19970                 cls : 'dropdown-menu'
19971             }
19972         }
19973         
19974         return cfg;
19975     },
19976     
19977     onRender : function(ct, position)
19978     {
19979         this.isSubMenu = ct.hasClass('dropdown-submenu');
19980         
19981         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
19982     },
19983     
19984     initEvents : function() 
19985     {
19986         if(this.isSubMenu){
19987             return;
19988         }
19989         
19990         this.hidden = true;
19991         
19992         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
19993         this.triggerEl.on('click', this.onTriggerPress, this);
19994         
19995         this.buttonEl = this.el.select('button.dropdown-button', true).first();
19996         this.buttonEl.on('click', this.onClick, this);
19997         
19998     },
19999     
20000     list : function()
20001     {
20002         if(this.isSubMenu){
20003             return this.el;
20004         }
20005         
20006         return this.el.select('ul.dropdown-menu', true).first();
20007     },
20008     
20009     onClick : function(e)
20010     {
20011         this.fireEvent("click", this, e);
20012     },
20013     
20014     onTriggerPress  : function(e)
20015     {   
20016         if (this.isVisible()) {
20017             this.hide();
20018         } else {
20019             this.show();
20020         }
20021     },
20022     
20023     isVisible : function(){
20024         return !this.hidden;
20025     },
20026     
20027     show : function()
20028     {
20029         this.fireEvent("beforeshow", this);
20030         
20031         this.hidden = false;
20032         this.el.addClass('open');
20033         
20034         Roo.get(document).on("mouseup", this.onMouseUp, this);
20035         
20036         this.fireEvent("show", this);
20037         
20038         
20039     },
20040     
20041     hide : function()
20042     {
20043         this.fireEvent("beforehide", this);
20044         
20045         this.hidden = true;
20046         this.el.removeClass('open');
20047         
20048         Roo.get(document).un("mouseup", this.onMouseUp);
20049         
20050         this.fireEvent("hide", this);
20051     },
20052     
20053     onMouseUp : function()
20054     {
20055         this.hide();
20056     }
20057     
20058 });
20059
20060  
20061  /*
20062  * - LGPL
20063  *
20064  * menu item
20065  * 
20066  */
20067 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20068
20069 /**
20070  * @class Roo.bootstrap.menu.Item
20071  * @extends Roo.bootstrap.Component
20072  * Bootstrap MenuItem class
20073  * @cfg {Boolean} submenu (true | false) default false
20074  * @cfg {String} html text of the item
20075  * @cfg {String} href the link
20076  * @cfg {Boolean} disable (true | false) default false
20077  * @cfg {Boolean} preventDefault (true | false) default true
20078  * @cfg {String} icon Font awesome icon
20079  * @cfg {String} pos Submenu align to (left | right) default right 
20080  * 
20081  * 
20082  * @constructor
20083  * Create a new Item
20084  * @param {Object} config The config object
20085  */
20086
20087
20088 Roo.bootstrap.menu.Item = function(config){
20089     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
20090     this.addEvents({
20091         /**
20092          * @event mouseover
20093          * Fires when the mouse is hovering over this menu
20094          * @param {Roo.bootstrap.menu.Item} this
20095          * @param {Roo.EventObject} e
20096          */
20097         mouseover : true,
20098         /**
20099          * @event mouseout
20100          * Fires when the mouse exits this menu
20101          * @param {Roo.bootstrap.menu.Item} this
20102          * @param {Roo.EventObject} e
20103          */
20104         mouseout : true,
20105         // raw events
20106         /**
20107          * @event click
20108          * The raw click event for the entire grid.
20109          * @param {Roo.EventObject} e
20110          */
20111         click : true
20112     });
20113 };
20114
20115 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
20116     
20117     submenu : false,
20118     href : '',
20119     html : '',
20120     preventDefault: true,
20121     disable : false,
20122     icon : false,
20123     pos : 'right',
20124     
20125     getAutoCreate : function()
20126     {
20127         var text = [
20128             {
20129                 tag : 'span',
20130                 cls : 'roo-menu-item-text',
20131                 html : this.html
20132             }
20133         ];
20134         
20135         if(this.icon){
20136             text.unshift({
20137                 tag : 'i',
20138                 cls : 'fa ' + this.icon
20139             })
20140         }
20141         
20142         var cfg = {
20143             tag : 'li',
20144             cn : [
20145                 {
20146                     tag : 'a',
20147                     href : this.href || '#',
20148                     cn : text
20149                 }
20150             ]
20151         };
20152         
20153         if(this.disable){
20154             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
20155         }
20156         
20157         if(this.submenu){
20158             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
20159             
20160             if(this.pos == 'left'){
20161                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
20162             }
20163         }
20164         
20165         return cfg;
20166     },
20167     
20168     initEvents : function() 
20169     {
20170         this.el.on('mouseover', this.onMouseOver, this);
20171         this.el.on('mouseout', this.onMouseOut, this);
20172         
20173         this.el.select('a', true).first().on('click', this.onClick, this);
20174         
20175     },
20176     
20177     onClick : function(e)
20178     {
20179         if(this.preventDefault){
20180             e.preventDefault();
20181         }
20182         
20183         this.fireEvent("click", this, e);
20184     },
20185     
20186     onMouseOver : function(e)
20187     {
20188         if(this.submenu && this.pos == 'left'){
20189             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
20190         }
20191         
20192         this.fireEvent("mouseover", this, e);
20193     },
20194     
20195     onMouseOut : function(e)
20196     {
20197         this.fireEvent("mouseout", this, e);
20198     }
20199 });
20200
20201  
20202
20203  /*
20204  * - LGPL
20205  *
20206  * menu separator
20207  * 
20208  */
20209 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20210
20211 /**
20212  * @class Roo.bootstrap.menu.Separator
20213  * @extends Roo.bootstrap.Component
20214  * Bootstrap Separator class
20215  * 
20216  * @constructor
20217  * Create a new Separator
20218  * @param {Object} config The config object
20219  */
20220
20221
20222 Roo.bootstrap.menu.Separator = function(config){
20223     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
20224 };
20225
20226 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
20227     
20228     getAutoCreate : function(){
20229         var cfg = {
20230             tag : 'li',
20231             cls: 'divider'
20232         };
20233         
20234         return cfg;
20235     }
20236    
20237 });
20238
20239  
20240
20241