roojs-bootstrap.js
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * 
20  * @constructor
21  * Do not use directly - it does not do anything..
22  * @param {Object} config The config object
23  */
24
25
26
27 Roo.bootstrap.Component = function(config){
28     Roo.bootstrap.Component.superclass.constructor.call(this, config);
29 };
30
31 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
32     
33     
34     allowDomMove : false, // to stop relocations in parent onRender...
35     
36     cls : false,
37     
38     style : false,
39     
40     autoCreate : false,
41     
42     initEvents : function() {  },
43     
44     xattr : false,
45     
46     parentId : false,
47     
48     can_build_overlaid : true,
49     
50     dataId : false,
51     
52     name : false,
53     
54     parent: function() {
55         // returns the parent component..
56         return Roo.ComponentMgr.get(this.parentId)
57         
58         
59     },
60     
61     // private
62     onRender : function(ct, position)
63     {
64        // Roo.log("Call onRender: " + this.xtype);
65         
66         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
67         
68         if(this.el){
69             if (this.el.attr('xtype')) {
70                 this.el.attr('xtypex', this.el.attr('xtype'));
71                 this.el.dom.removeAttribute('xtype');
72                 
73                 this.initEvents();
74             }
75             
76             return;
77         }
78         
79          
80         
81         var cfg = Roo.apply({},  this.getAutoCreate());
82         cfg.id = Roo.id();
83         
84         // fill in the extra attributes 
85         if (this.xattr && typeof(this.xattr) =='object') {
86             for (var i in this.xattr) {
87                 cfg[i] = this.xattr[i];
88             }
89         }
90         
91         if(this.dataId){
92             cfg.dataId = this.dataId;
93         }
94         
95         if (this.cls) {
96             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
97         }
98         
99         if (this.style) { // fixme needs to support more complex style data.
100             cfg.style = this.style;
101         }
102         
103         if(this.name){
104             cfg.name = this.name;
105         }
106         
107         this.el = ct.createChild(cfg, position);
108         
109         if(this.tabIndex !== undefined){
110             this.el.dom.setAttribute('tabIndex', this.tabIndex);
111         }
112         this.initEvents();
113         
114         
115     },
116     
117     getChildContainer : function()
118     {
119         return this.el;
120     },
121     
122     
123     addxtype  : function(tree,cntr)
124     {
125         var cn = this;
126         
127         cn = Roo.factory(tree);
128            
129         cn.parentType = this.xtype; //??
130         cn.parentId = this.id;
131         
132         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
133         
134         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
135         
136         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
137         
138         var build_from_html =  Roo.XComponent.build_from_html;
139           
140         var is_body  = (tree.xtype == 'Body') ;
141           
142         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
143           
144         var self_cntr_el = Roo.get(this[cntr](false));
145         
146         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
147             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
148                 return this.addxtypeChild(tree,cntr);
149             }
150             
151             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
152                 
153             if(echild){
154                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
155             }
156             
157             Roo.log('skipping render');
158             return cn;
159             
160         }
161         
162         var ret = false;
163         
164         while (true) {
165             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
166             
167             if (!echild) {
168                 break;
169             }
170             
171             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
172                 break;
173             }
174             
175             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
176         }
177         return ret;
178     },
179     
180     addxtypeChild : function (tree, cntr)
181     {
182         Roo.log('addxtypeChild:' + cntr);
183         var cn = this;
184         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
185         
186         
187         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
188                     (typeof(tree['flexy:foreach']) != 'undefined');
189           
190         
191         
192         
193         // render the element if it's not BODY.
194         if (tree.xtype != 'Body') {
195            
196             cn = Roo.factory(tree);
197            
198             cn.parentType = this.xtype; //??
199             cn.parentId = this.id;
200             
201             var build_from_html =  Roo.XComponent.build_from_html;
202             
203             
204             // does the container contain child eleemnts with 'xtype' attributes.
205             // that match this xtype..
206             // note - when we render we create these as well..
207             // so we should check to see if body has xtype set.
208             if (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
209                
210                 var self_cntr_el = Roo.get(this[cntr](false));
211                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
212                 
213                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
214                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
215                   
216                   
217                   
218                     cn.el = echild;
219                   //  Roo.log("GOT");
220                     //echild.dom.removeAttribute('xtype');
221                 } else {
222                     Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
223                    
224                 }
225             }
226            
227             
228                
229             // if object has flexy:if - then it may or may not be rendered.
230             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
231                 // skip a flexy if element.
232                 Roo.log('skipping render');
233              } else {
234                  
235                 // actually if flexy:foreach is found, we really want to create 
236                 // multiple copies here...
237                 //Roo.log('render');
238                 //Roo.log(this[cntr]());
239                 cn.render(this[cntr](true));
240              }
241             // then add the element..
242         }
243         
244         
245         // handle the kids..
246         
247         var nitems = [];
248         /*
249         if (typeof (tree.menu) != 'undefined') {
250             tree.menu.parentType = cn.xtype;
251             tree.menu.triggerEl = cn.el;
252             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
253             
254         }
255         */
256         if (!tree.items || !tree.items.length) {
257             cn.items = nitems;
258             return cn;
259         }
260         var items = tree.items;
261         delete tree.items;
262         
263         //Roo.log(items.length);
264             // add the items..
265         for(var i =0;i < items.length;i++) {
266             nitems.push(cn.addxtype(Roo.apply({}, items[i])));
267         }
268         
269         cn.items = nitems;
270         
271         return cn;
272     }
273     
274     
275     
276     
277 });
278
279  /*
280  * - LGPL
281  *
282  * Body
283  * 
284  */
285
286 /**
287  * @class Roo.bootstrap.Body
288  * @extends Roo.bootstrap.Component
289  * Bootstrap Body class
290  * 
291  * @constructor
292  * Create a new body
293  * @param {Object} config The config object
294  */
295
296 Roo.bootstrap.Body = function(config){
297     Roo.bootstrap.Body.superclass.constructor.call(this, config);
298     this.el = Roo.get(document.body);
299     if (this.cls && this.cls.length) {
300         Roo.get(document.body).addClass(this.cls);
301     }
302 };
303
304 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
305       
306         autoCreate : {
307         cls: 'container'
308     },
309     onRender : function(ct, position)
310     {
311        /* Roo.log("Roo.bootstrap.Body - onRender");
312         if (this.cls && this.cls.length) {
313             Roo.get(document.body).addClass(this.cls);
314         }
315         // style??? xttr???
316         */
317     }
318     
319     
320  
321    
322 });
323
324  /*
325  * - LGPL
326  *
327  * button group
328  * 
329  */
330
331
332 /**
333  * @class Roo.bootstrap.ButtonGroup
334  * @extends Roo.bootstrap.Component
335  * Bootstrap ButtonGroup class
336  * @cfg {String} size lg | sm | xs (default empty normal)
337  * @cfg {String} align vertical | justified  (default none)
338  * @cfg {String} direction up | down (default down)
339  * @cfg {Boolean} toolbar false | true
340  * @cfg {Boolean} btn true | false
341  * 
342  * 
343  * @constructor
344  * Create a new Input
345  * @param {Object} config The config object
346  */
347
348 Roo.bootstrap.ButtonGroup = function(config){
349     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
350 };
351
352 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
353     
354     size: '',
355     align: '',
356     direction: '',
357     toolbar: false,
358     btn: true,
359
360     getAutoCreate : function(){
361         var cfg = {
362             cls: 'btn-group',
363             html : null
364         }
365         
366         cfg.html = this.html || cfg.html;
367         
368         if (this.toolbar) {
369             cfg = {
370                 cls: 'btn-toolbar',
371                 html: null
372             }
373             
374             return cfg;
375         }
376         
377         if (['vertical','justified'].indexOf(this.align)!==-1) {
378             cfg.cls = 'btn-group-' + this.align;
379             
380             if (this.align == 'justified') {
381                 console.log(this.items);
382             }
383         }
384         
385         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
386             cfg.cls += ' btn-group-' + this.size;
387         }
388         
389         if (this.direction == 'up') {
390             cfg.cls += ' dropup' ;
391         }
392         
393         return cfg;
394     }
395    
396 });
397
398  /*
399  * - LGPL
400  *
401  * button
402  * 
403  */
404
405 /**
406  * @class Roo.bootstrap.Button
407  * @extends Roo.bootstrap.Component
408  * Bootstrap Button class
409  * @cfg {String} html The button content
410  * @cfg {String} weight default (or empty) | primary | success | info | warning | danger | link
411  * @cfg {String} size empty | lg | sm | xs
412  * @cfg {String} tag empty | a | input | submit
413  * @cfg {String} href empty or href
414  * @cfg {Boolean} disabled false | true
415  * @cfg {Boolean} isClose false | true
416  * @cfg {String} glyphicon empty | adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out
417  * @cfg {String} badge text for badge
418  * @cfg {String} theme default (or empty) | glow
419  * @cfg {Boolean} inverse false | true
420  * @cfg {Boolean} toggle false | true
421  * @cfg {String} ontext text for on toggle state
422  * @cfg {String} offtext text for off toggle state
423  * @cfg {Boolean} defaulton true | false
424  * @cfg {Boolean} preventDefault (true | false) default true
425  * @cfg {Boolean} removeClass true | false remove the standard class..
426  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
427  * 
428  * @constructor
429  * Create a new button
430  * @param {Object} config The config object
431  */
432
433
434 Roo.bootstrap.Button = function(config){
435     Roo.bootstrap.Button.superclass.constructor.call(this, config);
436     this.addEvents({
437         // raw events
438         /**
439          * @event click
440          * When a butotn is pressed
441          * @param {Roo.EventObject} e
442          */
443         "click" : true,
444          /**
445          * @event toggle
446          * After the button has been toggles
447          * @param {Roo.EventObject} e
448          * @param {boolean} pressed (also available as button.pressed)
449          */
450         "toggle" : true
451     });
452 };
453
454 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
455     html: false,
456     active: false,
457     weight: '',
458     size: '',
459     tag: 'button',
460     href: '',
461     disabled: false,
462     isClose: false,
463     glyphicon: '',
464     badge: '',
465     theme: 'default',
466     inverse: false,
467     
468     toggle: false,
469     ontext: 'ON',
470     offtext: 'OFF',
471     defaulton: true,
472     preventDefault: true,
473     removeClass: false,
474     name: false,
475     target: false,
476     
477     
478     pressed : null,
479      
480     
481     getAutoCreate : function(){
482         
483         var cfg = {
484             tag : 'button',
485             cls : 'roo-button',
486             html: ''
487         };
488         
489         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
490             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
491             this.tag = 'button';
492         } else {
493             cfg.tag = this.tag;
494         }
495         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
496         
497         if (this.toggle == true) {
498             cfg={
499                 tag: 'div',
500                 cls: 'slider-frame roo-button',
501                 cn: [
502                     {
503                         tag: 'span',
504                         'data-on-text':'ON',
505                         'data-off-text':'OFF',
506                         cls: 'slider-button',
507                         html: this.offtext
508                     }
509                 ]
510             };
511             
512             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
513                 cfg.cls += ' '+this.weight;
514             }
515             
516             return cfg;
517         }
518         
519         if (this.isClose) {
520             cfg.cls += ' close';
521             
522             cfg["aria-hidden"] = true;
523             
524             cfg.html = "&times;";
525             
526             return cfg;
527         }
528         
529          
530         if (this.theme==='default') {
531             cfg.cls = 'btn roo-button';
532             
533             //if (this.parentType != 'Navbar') {
534             this.weight = this.weight.length ?  this.weight : 'default';
535             //}
536             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
537                 
538                 cfg.cls += ' btn-' + this.weight;
539             }
540         } else if (this.theme==='glow') {
541             
542             cfg.tag = 'a';
543             cfg.cls = 'btn-glow roo-button';
544             
545             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
546                 
547                 cfg.cls += ' ' + this.weight;
548             }
549         }
550    
551         
552         if (this.inverse) {
553             this.cls += ' inverse';
554         }
555         
556         
557         if (this.active) {
558             cfg.cls += ' active';
559         }
560         
561         if (this.disabled) {
562             cfg.disabled = 'disabled';
563         }
564         
565         if (this.items) {
566             Roo.log('changing to ul' );
567             cfg.tag = 'ul';
568             this.glyphicon = 'caret';
569         }
570         
571         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
572          
573         //gsRoo.log(this.parentType);
574         if (this.parentType === 'Navbar' && !this.parent().bar) {
575             Roo.log('changing to li?');
576             
577             cfg.tag = 'li';
578             
579             cfg.cls = '';
580             cfg.cn =  [{
581                 tag : 'a',
582                 cls : 'roo-button',
583                 html : this.html,
584                 href : this.href || '#'
585             }];
586             if (this.menu) {
587                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
588                 cfg.cls += ' dropdown';
589             }   
590             
591             delete cfg.html;
592             
593         }
594         
595        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
596         
597         if (this.glyphicon) {
598             cfg.html = ' ' + cfg.html;
599             
600             cfg.cn = [
601                 {
602                     tag: 'span',
603                     cls: 'glyphicon glyphicon-' + this.glyphicon
604                 }
605             ];
606         }
607         
608         if (this.badge) {
609             cfg.html += ' ';
610             
611             cfg.tag = 'a';
612             
613 //            cfg.cls='btn roo-button';
614             
615             cfg.href=this.href;
616             
617             var value = cfg.html;
618             
619             if(this.glyphicon){
620                 value = {
621                             tag: 'span',
622                             cls: 'glyphicon glyphicon-' + this.glyphicon,
623                             html: this.html
624                         };
625                 
626             }
627             
628             cfg.cn = [
629                 value,
630                 {
631                     tag: 'span',
632                     cls: 'badge',
633                     html: this.badge
634                 }
635             ];
636             
637             cfg.html='';
638         }
639         
640         if (this.menu) {
641             cfg.cls += ' dropdown';
642             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
643         }
644         
645         if (cfg.tag !== 'a' && this.href !== '') {
646             throw "Tag must be a to set href.";
647         } else if (this.href.length > 0) {
648             cfg.href = this.href;
649         }
650         
651         if(this.removeClass){
652             cfg.cls = '';
653         }
654         
655         if(this.target){
656             cfg.target = this.target;
657         }
658         
659         return cfg;
660     },
661     initEvents: function() {
662        // Roo.log('init events?');
663 //        Roo.log(this.el.dom);
664         // add the menu...
665         
666         if (typeof (this.menu) != 'undefined') {
667             this.menu.parentType = this.xtype;
668             this.menu.triggerEl = this.el;
669             this.addxtype(Roo.apply({}, this.menu));
670         }
671
672
673        if (this.el.hasClass('roo-button')) {
674             this.el.on('click', this.onClick, this);
675        } else {
676             this.el.select('.roo-button').on('click', this.onClick, this);
677        }
678        
679        if(this.removeClass){
680            this.el.on('click', this.onClick, this);
681        }
682        
683        this.el.enableDisplayMode();
684         
685     },
686     onClick : function(e)
687     {
688         if (this.disabled) {
689             return;
690         }
691         
692         Roo.log('button on click ');
693         if(this.preventDefault){
694             e.preventDefault();
695         }
696         if (this.pressed === true || this.pressed === false) {
697             this.pressed = !this.pressed;
698             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
699             this.fireEvent('toggle', this, e, this.pressed);
700         }
701         
702         
703         this.fireEvent('click', this, e);
704     },
705     
706     /**
707      * Enables this button
708      */
709     enable : function()
710     {
711         this.disabled = false;
712         this.el.removeClass('disabled');
713     },
714     
715     /**
716      * Disable this button
717      */
718     disable : function()
719     {
720         this.disabled = true;
721         this.el.addClass('disabled');
722     },
723      /**
724      * sets the active state on/off, 
725      * @param {Boolean} state (optional) Force a particular state
726      */
727     setActive : function(v) {
728         
729         this.el[v ? 'addClass' : 'removeClass']('active');
730     },
731      /**
732      * toggles the current active state 
733      */
734     toggleActive : function()
735     {
736        var active = this.el.hasClass('active');
737        this.setActive(!active);
738        
739         
740     },
741     setText : function(str)
742     {
743         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
744     },
745     getText : function()
746     {
747         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
748     },
749     hide: function() {
750        
751      
752         this.el.hide();   
753     },
754     show: function() {
755        
756         this.el.show();   
757     }
758     
759     
760 });
761
762  /*
763  * - LGPL
764  *
765  * column
766  * 
767  */
768
769 /**
770  * @class Roo.bootstrap.Column
771  * @extends Roo.bootstrap.Component
772  * Bootstrap Column class
773  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
774  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
775  * @cfg {Number} md colspan out of 12 for computer-sized screens
776  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
777  * @cfg {String} html content of column.
778  * 
779  * @constructor
780  * Create a new Column
781  * @param {Object} config The config object
782  */
783
784 Roo.bootstrap.Column = function(config){
785     Roo.bootstrap.Column.superclass.constructor.call(this, config);
786 };
787
788 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
789     
790     xs: null,
791     sm: null,
792     md: null,
793     lg: null,
794     html: '',
795     offset: 0,
796     
797     getAutoCreate : function(){
798         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
799         
800         cfg = {
801             tag: 'div',
802             cls: 'column'
803         };
804         
805         var settings=this;
806         ['xs','sm','md','lg'].map(function(size){
807             if (settings[size]) {
808                 cfg.cls += ' col-' + size + '-' + settings[size];
809             }
810         });
811         if (this.html.length) {
812             cfg.html = this.html;
813         }
814         
815         return cfg;
816     }
817    
818 });
819
820  
821
822  /*
823  * - LGPL
824  *
825  * page container.
826  * 
827  */
828
829
830 /**
831  * @class Roo.bootstrap.Container
832  * @extends Roo.bootstrap.Component
833  * Bootstrap Container class
834  * @cfg {Boolean} jumbotron is it a jumbotron element
835  * @cfg {String} html content of element
836  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
837  * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
838  * @cfg {String} header content of header (for panel)
839  * @cfg {String} footer content of footer (for panel)
840  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
841  * @cfg {String} tag (header|aside|section) type of HTML tag.
842
843  *     
844  * @constructor
845  * Create a new Container
846  * @param {Object} config The config object
847  */
848
849 Roo.bootstrap.Container = function(config){
850     Roo.bootstrap.Container.superclass.constructor.call(this, config);
851 };
852
853 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
854     
855     jumbotron : false,
856     well: '',
857     panel : '',
858     header: '',
859     footer : '',
860     sticky: '',
861     tag : false,
862   
863      
864     getChildContainer : function() {
865         
866         if(!this.el){
867             return false;
868         }
869         
870         if (this.panel.length) {
871             return this.el.select('.panel-body',true).first();
872         }
873         
874         return this.el;
875     },
876     
877     
878     getAutoCreate : function(){
879         
880         var cfg = {
881             tag : this.tag || 'div',
882             html : '',
883             cls : ''
884         };
885         if (this.jumbotron) {
886             cfg.cls = 'jumbotron';
887         }
888         // - this is applied by the parent..
889         //if (this.cls) {
890         //    cfg.cls = this.cls + '';
891         //}
892         
893         if (this.sticky.length) {
894             
895             var bd = Roo.get(document.body);
896             if (!bd.hasClass('bootstrap-sticky')) {
897                 bd.addClass('bootstrap-sticky');
898                 Roo.select('html',true).setStyle('height', '100%');
899             }
900              
901             cfg.cls += 'bootstrap-sticky-' + this.sticky;
902         }
903         
904         
905         if (this.well.length) {
906             switch (this.well) {
907                 case 'lg':
908                 case 'sm':
909                     cfg.cls +=' well well-' +this.well;
910                     break;
911                 default:
912                     cfg.cls +=' well';
913                     break;
914             }
915         }
916         
917         var body = cfg;
918         
919         if (this.panel.length) {
920             cfg.cls += ' panel panel-' + this.panel;
921             cfg.cn = [];
922             if (this.header.length) {
923                 cfg.cn.push({
924                     
925                     cls : 'panel-heading',
926                     cn : [{
927                         tag: 'h3',
928                         cls : 'panel-title',
929                         html : this.header
930                     }]
931                     
932                 });
933             }
934             body = false;
935             cfg.cn.push({
936                 cls : 'panel-body',
937                 html : this.html
938             });
939             
940             
941             if (this.footer.length) {
942                 cfg.cn.push({
943                     cls : 'panel-footer',
944                     html : this.footer
945                     
946                 });
947             }
948             
949         }
950         
951         if (body) {
952             body.html = this.html || cfg.html;
953         }
954         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
955             cfg.cls =  'container';
956         }
957         
958         return cfg;
959     },
960     
961     titleEl : function()
962     {
963         if(!this.el || !this.panel.length || !this.header.length){
964             return;
965         }
966         
967         return this.el.select('.panel-title',true).first();
968     },
969     
970     setTitle : function(v)
971     {
972         var titleEl = this.titleEl();
973         
974         if(!titleEl){
975             return;
976         }
977         
978         titleEl.dom.innerHTML = v;
979     },
980     
981     getTitle : function()
982     {
983         
984         var titleEl = this.titleEl();
985         
986         if(!titleEl){
987             return '';
988         }
989         
990         return titleEl.dom.innerHTML;
991     }
992    
993 });
994
995  /*
996  * - LGPL
997  *
998  * image
999  * 
1000  */
1001
1002
1003 /**
1004  * @class Roo.bootstrap.Img
1005  * @extends Roo.bootstrap.Component
1006  * Bootstrap Img class
1007  * @cfg {Boolean} imgResponsive false | true
1008  * @cfg {String} border rounded | circle | thumbnail
1009  * @cfg {String} src image source
1010  * @cfg {String} alt image alternative text
1011  * @cfg {String} href a tag href
1012  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1013  * 
1014  * @constructor
1015  * Create a new Input
1016  * @param {Object} config The config object
1017  */
1018
1019 Roo.bootstrap.Img = function(config){
1020     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1021     
1022     this.addEvents({
1023         // img events
1024         /**
1025          * @event click
1026          * The img click event for the img.
1027          * @param {Roo.EventObject} e
1028          */
1029         "click" : true
1030     });
1031 };
1032
1033 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1034     
1035     imgResponsive: true,
1036     border: '',
1037     src: '',
1038     href: false,
1039     target: false,
1040
1041     getAutoCreate : function(){
1042         
1043         var cfg = {
1044             tag: 'img',
1045             cls: (this.imgResponsive) ? 'img-responsive' : '',
1046             html : null
1047         }
1048         
1049         cfg.html = this.html || cfg.html;
1050         
1051         cfg.src = this.src || cfg.src;
1052         
1053         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1054             cfg.cls += ' img-' + this.border;
1055         }
1056         
1057         if(this.alt){
1058             cfg.alt = this.alt;
1059         }
1060         
1061         if(this.href){
1062             var a = {
1063                 tag: 'a',
1064                 href: this.href,
1065                 cn: [
1066                     cfg
1067                 ]
1068             }
1069             
1070             if(this.target){
1071                 a.target = this.target;
1072             }
1073             
1074         }
1075         
1076         
1077         return (this.href) ? a : cfg;
1078     },
1079     
1080     initEvents: function() {
1081         
1082         if(!this.href){
1083             this.el.on('click', this.onClick, this);
1084         }
1085     },
1086     
1087     onClick : function(e)
1088     {
1089         Roo.log('img onclick');
1090         this.fireEvent('click', this, e);
1091     }
1092    
1093 });
1094
1095  /*
1096  * - LGPL
1097  *
1098  * image
1099  * 
1100  */
1101
1102
1103 /**
1104  * @class Roo.bootstrap.Link
1105  * @extends Roo.bootstrap.Component
1106  * Bootstrap Link Class
1107  * @cfg {String} alt image alternative text
1108  * @cfg {String} href a tag href
1109  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1110  * @cfg {String} html the content of the link.
1111  * @cfg {Boolean} preventDefault (true | false) default false
1112
1113  * 
1114  * @constructor
1115  * Create a new Input
1116  * @param {Object} config The config object
1117  */
1118
1119 Roo.bootstrap.Link = function(config){
1120     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1121     
1122     this.addEvents({
1123         // img events
1124         /**
1125          * @event click
1126          * The img click event for the img.
1127          * @param {Roo.EventObject} e
1128          */
1129         "click" : true
1130     });
1131 };
1132
1133 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1134     
1135     href: false,
1136     target: false,
1137     preventDefault: false,
1138
1139     getAutoCreate : function(){
1140         
1141         var cfg = {
1142             tag: 'a',
1143             html : this.html || 'html-missing'
1144         }
1145         
1146         
1147         if(this.alt){
1148             cfg.alt = this.alt;
1149         }
1150         cfg.href = this.href || '#';
1151         if(this.target){
1152             cfg.target = this.target;
1153         }
1154         
1155         return cfg;
1156     },
1157     
1158     initEvents: function() {
1159         
1160         if(!this.href || this.preventDefault){
1161             this.el.on('click', this.onClick, this);
1162         }
1163     },
1164     
1165     onClick : function(e)
1166     {
1167         if(this.preventDefault){
1168             e.preventDefault();
1169         }
1170         //Roo.log('img onclick');
1171         this.fireEvent('click', this, e);
1172     }
1173    
1174 });
1175
1176  /*
1177  * - LGPL
1178  *
1179  * header
1180  * 
1181  */
1182
1183 /**
1184  * @class Roo.bootstrap.Header
1185  * @extends Roo.bootstrap.Component
1186  * Bootstrap Header class
1187  * @cfg {String} html content of header
1188  * @cfg {Number} level (1|2|3|4|5|6) default 1
1189  * 
1190  * @constructor
1191  * Create a new Header
1192  * @param {Object} config The config object
1193  */
1194
1195
1196 Roo.bootstrap.Header  = function(config){
1197     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1198 };
1199
1200 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1201     
1202     //href : false,
1203     html : false,
1204     level : 1,
1205     
1206     
1207     
1208     getAutoCreate : function(){
1209         
1210         var cfg = {
1211             tag: 'h' + (1 *this.level),
1212             html: this.html || 'fill in html'
1213         } ;
1214         
1215         return cfg;
1216     }
1217    
1218 });
1219
1220  
1221
1222  /*
1223  * Based on:
1224  * Ext JS Library 1.1.1
1225  * Copyright(c) 2006-2007, Ext JS, LLC.
1226  *
1227  * Originally Released Under LGPL - original licence link has changed is not relivant.
1228  *
1229  * Fork - LGPL
1230  * <script type="text/javascript">
1231  */
1232  
1233 /**
1234  * @class Roo.bootstrap.MenuMgr
1235  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1236  * @singleton
1237  */
1238 Roo.bootstrap.MenuMgr = function(){
1239    var menus, active, groups = {}, attached = false, lastShow = new Date();
1240
1241    // private - called when first menu is created
1242    function init(){
1243        menus = {};
1244        active = new Roo.util.MixedCollection();
1245        Roo.get(document).addKeyListener(27, function(){
1246            if(active.length > 0){
1247                hideAll();
1248            }
1249        });
1250    }
1251
1252    // private
1253    function hideAll(){
1254        if(active && active.length > 0){
1255            var c = active.clone();
1256            c.each(function(m){
1257                m.hide();
1258            });
1259        }
1260    }
1261
1262    // private
1263    function onHide(m){
1264        active.remove(m);
1265        if(active.length < 1){
1266            Roo.get(document).un("mouseup", onMouseDown);
1267             
1268            attached = false;
1269        }
1270    }
1271
1272    // private
1273    function onShow(m){
1274        var last = active.last();
1275        lastShow = new Date();
1276        active.add(m);
1277        if(!attached){
1278           Roo.get(document).on("mouseup", onMouseDown);
1279            
1280            attached = true;
1281        }
1282        if(m.parentMenu){
1283           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1284           m.parentMenu.activeChild = m;
1285        }else if(last && last.isVisible()){
1286           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1287        }
1288    }
1289
1290    // private
1291    function onBeforeHide(m){
1292        if(m.activeChild){
1293            m.activeChild.hide();
1294        }
1295        if(m.autoHideTimer){
1296            clearTimeout(m.autoHideTimer);
1297            delete m.autoHideTimer;
1298        }
1299    }
1300
1301    // private
1302    function onBeforeShow(m){
1303        var pm = m.parentMenu;
1304        if(!pm && !m.allowOtherMenus){
1305            hideAll();
1306        }else if(pm && pm.activeChild && active != m){
1307            pm.activeChild.hide();
1308        }
1309    }
1310
1311    // private
1312    function onMouseDown(e){
1313         Roo.log("on MouseDown");
1314         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
1315            hideAll();
1316         }
1317         
1318         
1319    }
1320
1321    // private
1322    function onBeforeCheck(mi, state){
1323        if(state){
1324            var g = groups[mi.group];
1325            for(var i = 0, l = g.length; i < l; i++){
1326                if(g[i] != mi){
1327                    g[i].setChecked(false);
1328                }
1329            }
1330        }
1331    }
1332
1333    return {
1334
1335        /**
1336         * Hides all menus that are currently visible
1337         */
1338        hideAll : function(){
1339             hideAll();  
1340        },
1341
1342        // private
1343        register : function(menu){
1344            if(!menus){
1345                init();
1346            }
1347            menus[menu.id] = menu;
1348            menu.on("beforehide", onBeforeHide);
1349            menu.on("hide", onHide);
1350            menu.on("beforeshow", onBeforeShow);
1351            menu.on("show", onShow);
1352            var g = menu.group;
1353            if(g && menu.events["checkchange"]){
1354                if(!groups[g]){
1355                    groups[g] = [];
1356                }
1357                groups[g].push(menu);
1358                menu.on("checkchange", onCheck);
1359            }
1360        },
1361
1362         /**
1363          * Returns a {@link Roo.menu.Menu} object
1364          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1365          * be used to generate and return a new Menu instance.
1366          */
1367        get : function(menu){
1368            if(typeof menu == "string"){ // menu id
1369                return menus[menu];
1370            }else if(menu.events){  // menu instance
1371                return menu;
1372            }
1373            /*else if(typeof menu.length == 'number'){ // array of menu items?
1374                return new Roo.bootstrap.Menu({items:menu});
1375            }else{ // otherwise, must be a config
1376                return new Roo.bootstrap.Menu(menu);
1377            }
1378            */
1379            return false;
1380        },
1381
1382        // private
1383        unregister : function(menu){
1384            delete menus[menu.id];
1385            menu.un("beforehide", onBeforeHide);
1386            menu.un("hide", onHide);
1387            menu.un("beforeshow", onBeforeShow);
1388            menu.un("show", onShow);
1389            var g = menu.group;
1390            if(g && menu.events["checkchange"]){
1391                groups[g].remove(menu);
1392                menu.un("checkchange", onCheck);
1393            }
1394        },
1395
1396        // private
1397        registerCheckable : function(menuItem){
1398            var g = menuItem.group;
1399            if(g){
1400                if(!groups[g]){
1401                    groups[g] = [];
1402                }
1403                groups[g].push(menuItem);
1404                menuItem.on("beforecheckchange", onBeforeCheck);
1405            }
1406        },
1407
1408        // private
1409        unregisterCheckable : function(menuItem){
1410            var g = menuItem.group;
1411            if(g){
1412                groups[g].remove(menuItem);
1413                menuItem.un("beforecheckchange", onBeforeCheck);
1414            }
1415        }
1416    };
1417 }();/*
1418  * - LGPL
1419  *
1420  * menu
1421  * 
1422  */
1423
1424 /**
1425  * @class Roo.bootstrap.Menu
1426  * @extends Roo.bootstrap.Component
1427  * Bootstrap Menu class - container for MenuItems
1428  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1429  * 
1430  * @constructor
1431  * Create a new Menu
1432  * @param {Object} config The config object
1433  */
1434
1435
1436 Roo.bootstrap.Menu = function(config){
1437     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1438     if (this.registerMenu) {
1439         Roo.bootstrap.MenuMgr.register(this);
1440     }
1441     this.addEvents({
1442         /**
1443          * @event beforeshow
1444          * Fires before this menu is displayed
1445          * @param {Roo.menu.Menu} this
1446          */
1447         beforeshow : true,
1448         /**
1449          * @event beforehide
1450          * Fires before this menu is hidden
1451          * @param {Roo.menu.Menu} this
1452          */
1453         beforehide : true,
1454         /**
1455          * @event show
1456          * Fires after this menu is displayed
1457          * @param {Roo.menu.Menu} this
1458          */
1459         show : true,
1460         /**
1461          * @event hide
1462          * Fires after this menu is hidden
1463          * @param {Roo.menu.Menu} this
1464          */
1465         hide : true,
1466         /**
1467          * @event click
1468          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1469          * @param {Roo.menu.Menu} this
1470          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1471          * @param {Roo.EventObject} e
1472          */
1473         click : true,
1474         /**
1475          * @event mouseover
1476          * Fires when the mouse is hovering over this menu
1477          * @param {Roo.menu.Menu} this
1478          * @param {Roo.EventObject} e
1479          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1480          */
1481         mouseover : true,
1482         /**
1483          * @event mouseout
1484          * Fires when the mouse exits this menu
1485          * @param {Roo.menu.Menu} this
1486          * @param {Roo.EventObject} e
1487          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1488          */
1489         mouseout : true,
1490         /**
1491          * @event itemclick
1492          * Fires when a menu item contained in this menu is clicked
1493          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1494          * @param {Roo.EventObject} e
1495          */
1496         itemclick: true
1497     });
1498     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1499 };
1500
1501 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1502     
1503    /// html : false,
1504     //align : '',
1505     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1506     type: false,
1507     /**
1508      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1509      */
1510     registerMenu : true,
1511     
1512     menuItems :false, // stores the menu items..
1513     
1514     hidden:true,
1515     
1516     parentMenu : false,
1517     
1518     getChildContainer : function() {
1519         return this.el;  
1520     },
1521     
1522     getAutoCreate : function(){
1523          
1524         //if (['right'].indexOf(this.align)!==-1) {
1525         //    cfg.cn[1].cls += ' pull-right'
1526         //}
1527         
1528         
1529         var cfg = {
1530             tag : 'ul',
1531             cls : 'dropdown-menu' ,
1532             style : 'z-index:1000'
1533             
1534         }
1535         
1536         if (this.type === 'submenu') {
1537             cfg.cls = 'submenu active';
1538         }
1539         if (this.type === 'treeview') {
1540             cfg.cls = 'treeview-menu';
1541         }
1542         
1543         return cfg;
1544     },
1545     initEvents : function() {
1546         
1547        // Roo.log("ADD event");
1548        // Roo.log(this.triggerEl.dom);
1549         this.triggerEl.on('click', this.onTriggerPress, this);
1550         this.triggerEl.addClass('dropdown-toggle');
1551         this.el.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
1552
1553         this.el.on("mouseover", this.onMouseOver, this);
1554         this.el.on("mouseout", this.onMouseOut, this);
1555         
1556         
1557     },
1558     findTargetItem : function(e){
1559         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
1560         if(!t){
1561             return false;
1562         }
1563         //Roo.log(t);         Roo.log(t.id);
1564         if(t && t.id){
1565             //Roo.log(this.menuitems);
1566             return this.menuitems.get(t.id);
1567             
1568             //return this.items.get(t.menuItemId);
1569         }
1570         
1571         return false;
1572     },
1573     onClick : function(e){
1574         Roo.log("menu.onClick");
1575         var t = this.findTargetItem(e);
1576         if(!t){
1577             return;
1578         }
1579         Roo.log(e);
1580         /*
1581         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
1582             if(t == this.activeItem && t.shouldDeactivate(e)){
1583                 this.activeItem.deactivate();
1584                 delete this.activeItem;
1585                 return;
1586             }
1587             if(t.canActivate){
1588                 this.setActiveItem(t, true);
1589             }
1590             return;
1591             
1592             
1593         }
1594         */
1595         Roo.log('pass click event');
1596         
1597         t.onClick(e);
1598         
1599         this.fireEvent("click", this, t, e);
1600         
1601         this.hide();
1602     },
1603      onMouseOver : function(e){
1604         var t  = this.findTargetItem(e);
1605         //Roo.log(t);
1606         //if(t){
1607         //    if(t.canActivate && !t.disabled){
1608         //        this.setActiveItem(t, true);
1609         //    }
1610         //}
1611         
1612         this.fireEvent("mouseover", this, e, t);
1613     },
1614     isVisible : function(){
1615         return !this.hidden;
1616     },
1617      onMouseOut : function(e){
1618         var t  = this.findTargetItem(e);
1619         
1620         //if(t ){
1621         //    if(t == this.activeItem && t.shouldDeactivate(e)){
1622         //        this.activeItem.deactivate();
1623         //        delete this.activeItem;
1624         //    }
1625         //}
1626         this.fireEvent("mouseout", this, e, t);
1627     },
1628     
1629     
1630     /**
1631      * Displays this menu relative to another element
1632      * @param {String/HTMLElement/Roo.Element} element The element to align to
1633      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
1634      * the element (defaults to this.defaultAlign)
1635      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1636      */
1637     show : function(el, pos, parentMenu){
1638         this.parentMenu = parentMenu;
1639         if(!this.el){
1640             this.render();
1641         }
1642         this.fireEvent("beforeshow", this);
1643         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
1644     },
1645      /**
1646      * Displays this menu at a specific xy position
1647      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
1648      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1649      */
1650     showAt : function(xy, parentMenu, /* private: */_e){
1651         this.parentMenu = parentMenu;
1652         if(!this.el){
1653             this.render();
1654         }
1655         if(_e !== false){
1656             this.fireEvent("beforeshow", this);
1657             
1658             //xy = this.el.adjustForConstraints(xy);
1659         }
1660         //this.el.setXY(xy);
1661         //this.el.show();
1662         this.hideMenuItems();
1663         this.hidden = false;
1664         this.triggerEl.addClass('open');
1665         this.focus();
1666         this.fireEvent("show", this);
1667     },
1668     
1669     focus : function(){
1670         return;
1671         if(!this.hidden){
1672             this.doFocus.defer(50, this);
1673         }
1674     },
1675
1676     doFocus : function(){
1677         if(!this.hidden){
1678             this.focusEl.focus();
1679         }
1680     },
1681
1682     /**
1683      * Hides this menu and optionally all parent menus
1684      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
1685      */
1686     hide : function(deep){
1687         
1688         this.hideMenuItems();
1689         if(this.el && this.isVisible()){
1690             this.fireEvent("beforehide", this);
1691             if(this.activeItem){
1692                 this.activeItem.deactivate();
1693                 this.activeItem = null;
1694             }
1695             this.triggerEl.removeClass('open');;
1696             this.hidden = true;
1697             this.fireEvent("hide", this);
1698         }
1699         if(deep === true && this.parentMenu){
1700             this.parentMenu.hide(true);
1701         }
1702     },
1703     
1704     onTriggerPress  : function(e)
1705     {
1706         
1707         Roo.log('trigger press');
1708         //Roo.log(e.getTarget());
1709        // Roo.log(this.triggerEl.dom);
1710         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
1711             return;
1712         }
1713         if (this.isVisible()) {
1714             Roo.log('hide');
1715             this.hide();
1716         } else {
1717             this.show(this.triggerEl, false, false);
1718         }
1719         
1720         
1721     },
1722     
1723          
1724        
1725     
1726     hideMenuItems : function()
1727     {
1728         //$(backdrop).remove()
1729         Roo.select('.open',true).each(function(aa) {
1730             
1731             aa.removeClass('open');
1732           //var parent = getParent($(this))
1733           //var relatedTarget = { relatedTarget: this }
1734           
1735            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
1736           //if (e.isDefaultPrevented()) return
1737            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
1738         })
1739     },
1740     addxtypeChild : function (tree, cntr) {
1741         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
1742           
1743         this.menuitems.add(comp);
1744         return comp;
1745
1746     },
1747     getEl : function()
1748     {
1749         Roo.log(this.el);
1750         return this.el;
1751     }
1752 });
1753
1754  
1755  /*
1756  * - LGPL
1757  *
1758  * menu item
1759  * 
1760  */
1761
1762
1763 /**
1764  * @class Roo.bootstrap.MenuItem
1765  * @extends Roo.bootstrap.Component
1766  * Bootstrap MenuItem class
1767  * @cfg {String} html the menu label
1768  * @cfg {String} href the link
1769  * @cfg {Boolean} preventDefault (true | false) default true
1770  * 
1771  * 
1772  * @constructor
1773  * Create a new MenuItem
1774  * @param {Object} config The config object
1775  */
1776
1777
1778 Roo.bootstrap.MenuItem = function(config){
1779     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
1780     this.addEvents({
1781         // raw events
1782         /**
1783          * @event click
1784          * The raw click event for the entire grid.
1785          * @param {Roo.EventObject} e
1786          */
1787         "click" : true
1788     });
1789 };
1790
1791 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
1792     
1793     href : false,
1794     html : false,
1795     preventDefault: true,
1796     
1797     getAutoCreate : function(){
1798         var cfg= {
1799             tag: 'li',
1800             cls: 'dropdown-menu-item',
1801             cn: [
1802                     {
1803                         tag : 'a',
1804                         href : '#',
1805                         html : 'Link'
1806                     }
1807                 ]
1808         };
1809         if (this.parent().type == 'treeview') {
1810             cfg.cls = 'treeview-menu';
1811         }
1812         
1813         cfg.cn[0].href = this.href || cfg.cn[0].href ;
1814         cfg.cn[0].html = this.html || cfg.cn[0].html ;
1815         return cfg;
1816     },
1817     
1818     initEvents: function() {
1819         
1820         //this.el.select('a').on('click', this.onClick, this);
1821         
1822     },
1823     onClick : function(e)
1824     {
1825         Roo.log('item on click ');
1826         //if(this.preventDefault){
1827         //    e.preventDefault();
1828         //}
1829         //this.parent().hideMenuItems();
1830         
1831         this.fireEvent('click', this, e);
1832     },
1833     getEl : function()
1834     {
1835         return this.el;
1836     }
1837 });
1838
1839  
1840
1841  /*
1842  * - LGPL
1843  *
1844  * menu separator
1845  * 
1846  */
1847
1848
1849 /**
1850  * @class Roo.bootstrap.MenuSeparator
1851  * @extends Roo.bootstrap.Component
1852  * Bootstrap MenuSeparator class
1853  * 
1854  * @constructor
1855  * Create a new MenuItem
1856  * @param {Object} config The config object
1857  */
1858
1859
1860 Roo.bootstrap.MenuSeparator = function(config){
1861     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
1862 };
1863
1864 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
1865     
1866     getAutoCreate : function(){
1867         var cfg = {
1868             cls: 'divider',
1869             tag : 'li'
1870         };
1871         
1872         return cfg;
1873     }
1874    
1875 });
1876
1877  
1878
1879  
1880 /*
1881 <div class="modal fade">
1882   <div class="modal-dialog">
1883     <div class="modal-content">
1884       <div class="modal-header">
1885         <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
1886         <h4 class="modal-title">Modal title</h4>
1887       </div>
1888       <div class="modal-body">
1889         <p>One fine body&hellip;</p>
1890       </div>
1891       <div class="modal-footer">
1892         <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
1893         <button type="button" class="btn btn-primary">Save changes</button>
1894       </div>
1895     </div><!-- /.modal-content -->
1896   </div><!-- /.modal-dialog -->
1897 </div><!-- /.modal -->
1898 */
1899 /*
1900  * - LGPL
1901  *
1902  * page contgainer.
1903  * 
1904  */
1905
1906 /**
1907  * @class Roo.bootstrap.Modal
1908  * @extends Roo.bootstrap.Component
1909  * Bootstrap Modal class
1910  * @cfg {String} title Title of dialog
1911  * @cfg {Boolean} specificTitle (true|false) default false
1912  * @cfg {Array} buttons Array of buttons or standard button set..
1913  * @cfg {String} buttonPosition (left|right|center) default right
1914  * 
1915  * @constructor
1916  * Create a new Modal Dialog
1917  * @param {Object} config The config object
1918  */
1919
1920 Roo.bootstrap.Modal = function(config){
1921     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
1922     this.addEvents({
1923         // raw events
1924         /**
1925          * @event btnclick
1926          * The raw btnclick event for the button
1927          * @param {Roo.EventObject} e
1928          */
1929         "btnclick" : true
1930     });
1931     this.buttons = this.buttons || [];
1932 };
1933
1934 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
1935     
1936     title : 'test dialog',
1937    
1938     buttons : false,
1939     
1940     // set on load...
1941     body:  false,
1942     
1943     specificTitle: false,
1944     
1945     buttonPosition: 'right',
1946     
1947     onRender : function(ct, position)
1948     {
1949         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
1950      
1951         if(!this.el){
1952             var cfg = Roo.apply({},  this.getAutoCreate());
1953             cfg.id = Roo.id();
1954             //if(!cfg.name){
1955             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
1956             //}
1957             //if (!cfg.name.length) {
1958             //    delete cfg.name;
1959            // }
1960             if (this.cls) {
1961                 cfg.cls += ' ' + this.cls;
1962             }
1963             if (this.style) {
1964                 cfg.style = this.style;
1965             }
1966             this.el = Roo.get(document.body).createChild(cfg, position);
1967         }
1968         //var type = this.el.dom.type;
1969         
1970         if(this.tabIndex !== undefined){
1971             this.el.dom.setAttribute('tabIndex', this.tabIndex);
1972         }
1973         
1974         
1975         
1976         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
1977         this.maskEl.enableDisplayMode("block");
1978         this.maskEl.hide();
1979         //this.el.addClass("x-dlg-modal");
1980     
1981         if (this.buttons.length) {
1982             Roo.each(this.buttons, function(bb) {
1983                 b = Roo.apply({}, bb);
1984                 b.xns = b.xns || Roo.bootstrap;
1985                 b.xtype = b.xtype || 'Button';
1986                 if (typeof(b.listeners) == 'undefined') {
1987                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
1988                 }
1989                 
1990                 var btn = Roo.factory(b);
1991                 
1992                 btn.onRender(this.el.select('.modal-footer div').first());
1993                 
1994             },this);
1995         }
1996         // render the children.
1997         var nitems = [];
1998         
1999         if(typeof(this.items) != 'undefined'){
2000             var items = this.items;
2001             delete this.items;
2002
2003             for(var i =0;i < items.length;i++) {
2004                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2005             }
2006         }
2007         
2008         this.items = nitems;
2009         
2010         this.body = this.el.select('.modal-body',true).first();
2011         this.close = this.el.select('.modal-header .close', true).first();
2012         this.footer = this.el.select('.modal-footer',true).first();
2013         this.initEvents();
2014         //this.el.addClass([this.fieldClass, this.cls]);
2015         
2016     },
2017     getAutoCreate : function(){
2018         
2019         
2020         var bdy = {
2021                 cls : 'modal-body',
2022                 html : this.html || ''
2023         };
2024         
2025         var title = {
2026             tag: 'h4',
2027             cls : 'modal-title',
2028             html : this.title
2029         };
2030         
2031         if(this.specificTitle){
2032             title = this.title;
2033         };
2034         
2035         return modal = {
2036             cls: "modal fade",
2037             style : 'display: none',
2038             cn : [
2039                 {
2040                     cls: "modal-dialog",
2041                     cn : [
2042                         {
2043                             cls : "modal-content",
2044                             cn : [
2045                                 {
2046                                     cls : 'modal-header',
2047                                     cn : [
2048                                         {
2049                                             tag: 'button',
2050                                             cls : 'close',
2051                                             html : '&times'
2052                                         },
2053                                         title
2054                                     ]
2055                                 },
2056                                 bdy,
2057                                 {
2058                                     cls : 'modal-footer',
2059                                     cn : [
2060                                         {
2061                                             tag: 'div',
2062                                             cls: 'btn-' + this.buttonPosition
2063                                         }
2064                                     ]
2065                                     
2066                                 }
2067                                 
2068                                 
2069                             ]
2070                             
2071                         }
2072                     ]
2073                         
2074                 }
2075             ]
2076             
2077             
2078         };
2079           
2080     },
2081     getChildContainer : function() {
2082          
2083          return this.el.select('.modal-body',true).first();
2084         
2085     },
2086     getButtonContainer : function() {
2087          return this.el.select('.modal-footer div',true).first();
2088         
2089     },
2090     initEvents : function()
2091     {
2092         this.el.select('.modal-header .close').on('click', this.hide, this);
2093 //        
2094 //        this.addxtype(this);
2095     },
2096     show : function() {
2097         
2098         if (!this.rendered) {
2099             this.render();
2100         }
2101        
2102         this.el.addClass('on');
2103         this.el.removeClass('fade');
2104         this.el.setStyle('display', 'block');
2105         Roo.get(document.body).addClass("x-body-masked");
2106         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2107         this.maskEl.show();
2108         this.el.setStyle('zIndex', '10001');
2109         this.fireEvent('show', this);
2110         
2111         
2112     },
2113     hide : function()
2114     {
2115         Roo.log('Modal hide?!');
2116         this.maskEl.hide();
2117         Roo.get(document.body).removeClass("x-body-masked");
2118         this.el.removeClass('on');
2119         this.el.addClass('fade');
2120         this.el.setStyle('display', 'none');
2121         this.fireEvent('hide', this);
2122     },
2123     
2124     addButton : function(str, cb)
2125     {
2126          
2127         
2128         var b = Roo.apply({}, { html : str } );
2129         b.xns = b.xns || Roo.bootstrap;
2130         b.xtype = b.xtype || 'Button';
2131         if (typeof(b.listeners) == 'undefined') {
2132             b.listeners = { click : cb.createDelegate(this)  };
2133         }
2134         
2135         var btn = Roo.factory(b);
2136            
2137         btn.onRender(this.el.select('.modal-footer div').first());
2138         
2139         return btn;   
2140        
2141     },
2142     
2143     setDefaultButton : function(btn)
2144     {
2145         //this.el.select('.modal-footer').()
2146     },
2147     resizeTo: function(w,h)
2148     {
2149         // skip..
2150     },
2151     setContentSize  : function(w, h)
2152     {
2153         
2154     },
2155     onButtonClick: function(btn,e)
2156     {
2157         //Roo.log([a,b,c]);
2158         this.fireEvent('btnclick', btn.name, e);
2159     },
2160     setTitle: function(str) {
2161         this.el.select('.modal-title',true).first().dom.innerHTML = str;
2162         
2163     }
2164 });
2165
2166
2167 Roo.apply(Roo.bootstrap.Modal,  {
2168     /**
2169          * Button config that displays a single OK button
2170          * @type Object
2171          */
2172         OK :  [{
2173             name : 'ok',
2174             weight : 'primary',
2175             html : 'OK'
2176         }], 
2177         /**
2178          * Button config that displays Yes and No buttons
2179          * @type Object
2180          */
2181         YESNO : [
2182             {
2183                 name  : 'no',
2184                 html : 'No'
2185             },
2186             {
2187                 name  :'yes',
2188                 weight : 'primary',
2189                 html : 'Yes'
2190             }
2191         ],
2192         
2193         /**
2194          * Button config that displays OK and Cancel buttons
2195          * @type Object
2196          */
2197         OKCANCEL : [
2198             {
2199                name : 'cancel',
2200                 html : 'Cancel'
2201             },
2202             {
2203                 name : 'ok',
2204                 weight : 'primary',
2205                 html : 'OK'
2206             }
2207         ],
2208         /**
2209          * Button config that displays Yes, No and Cancel buttons
2210          * @type Object
2211          */
2212         YESNOCANCEL : [
2213             {
2214                 name : 'yes',
2215                 weight : 'primary',
2216                 html : 'Yes'
2217             },
2218             {
2219                 name : 'no',
2220                 html : 'No'
2221             },
2222             {
2223                 name : 'cancel',
2224                 html : 'Cancel'
2225             }
2226         ]
2227 });
2228  /*
2229  * - LGPL
2230  *
2231  * messagebox - can be used as a replace
2232  * 
2233  */
2234 /**
2235  * @class Roo.MessageBox
2236  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2237  * Example usage:
2238  *<pre><code>
2239 // Basic alert:
2240 Roo.Msg.alert('Status', 'Changes saved successfully.');
2241
2242 // Prompt for user data:
2243 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2244     if (btn == 'ok'){
2245         // process text value...
2246     }
2247 });
2248
2249 // Show a dialog using config options:
2250 Roo.Msg.show({
2251    title:'Save Changes?',
2252    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2253    buttons: Roo.Msg.YESNOCANCEL,
2254    fn: processResult,
2255    animEl: 'elId'
2256 });
2257 </code></pre>
2258  * @singleton
2259  */
2260 Roo.bootstrap.MessageBox = function(){
2261     var dlg, opt, mask, waitTimer;
2262     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2263     var buttons, activeTextEl, bwidth;
2264
2265     
2266     // private
2267     var handleButton = function(button){
2268         dlg.hide();
2269         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2270     };
2271
2272     // private
2273     var handleHide = function(){
2274         if(opt && opt.cls){
2275             dlg.el.removeClass(opt.cls);
2276         }
2277         //if(waitTimer){
2278         //    Roo.TaskMgr.stop(waitTimer);
2279         //    waitTimer = null;
2280         //}
2281     };
2282
2283     // private
2284     var updateButtons = function(b){
2285         var width = 0;
2286         if(!b){
2287             buttons["ok"].hide();
2288             buttons["cancel"].hide();
2289             buttons["yes"].hide();
2290             buttons["no"].hide();
2291             //dlg.footer.dom.style.display = 'none';
2292             return width;
2293         }
2294         dlg.footer.dom.style.display = '';
2295         for(var k in buttons){
2296             if(typeof buttons[k] != "function"){
2297                 if(b[k]){
2298                     buttons[k].show();
2299                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2300                     width += buttons[k].el.getWidth()+15;
2301                 }else{
2302                     buttons[k].hide();
2303                 }
2304             }
2305         }
2306         return width;
2307     };
2308
2309     // private
2310     var handleEsc = function(d, k, e){
2311         if(opt && opt.closable !== false){
2312             dlg.hide();
2313         }
2314         if(e){
2315             e.stopEvent();
2316         }
2317     };
2318
2319     return {
2320         /**
2321          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2322          * @return {Roo.BasicDialog} The BasicDialog element
2323          */
2324         getDialog : function(){
2325            if(!dlg){
2326                 dlg = new Roo.bootstrap.Modal( {
2327                     //draggable: true,
2328                     //resizable:false,
2329                     //constraintoviewport:false,
2330                     //fixedcenter:true,
2331                     //collapsible : false,
2332                     //shim:true,
2333                     //modal: true,
2334                   //  width:400,
2335                   //  height:100,
2336                     //buttonAlign:"center",
2337                     closeClick : function(){
2338                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2339                             handleButton("no");
2340                         }else{
2341                             handleButton("cancel");
2342                         }
2343                     }
2344                 });
2345                 dlg.render();
2346                 dlg.on("hide", handleHide);
2347                 mask = dlg.mask;
2348                 //dlg.addKeyListener(27, handleEsc);
2349                 buttons = {};
2350                 this.buttons = buttons;
2351                 var bt = this.buttonText;
2352                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2353                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2354                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2355                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2356                 Roo.log(buttons)
2357                 bodyEl = dlg.body.createChild({
2358
2359                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2360                         '<textarea class="roo-mb-textarea"></textarea>' +
2361                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
2362                 });
2363                 msgEl = bodyEl.dom.firstChild;
2364                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2365                 textboxEl.enableDisplayMode();
2366                 textboxEl.addKeyListener([10,13], function(){
2367                     if(dlg.isVisible() && opt && opt.buttons){
2368                         if(opt.buttons.ok){
2369                             handleButton("ok");
2370                         }else if(opt.buttons.yes){
2371                             handleButton("yes");
2372                         }
2373                     }
2374                 });
2375                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2376                 textareaEl.enableDisplayMode();
2377                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2378                 progressEl.enableDisplayMode();
2379                 var pf = progressEl.dom.firstChild;
2380                 if (pf) {
2381                     pp = Roo.get(pf.firstChild);
2382                     pp.setHeight(pf.offsetHeight);
2383                 }
2384                 
2385             }
2386             return dlg;
2387         },
2388
2389         /**
2390          * Updates the message box body text
2391          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2392          * the XHTML-compliant non-breaking space character '&amp;#160;')
2393          * @return {Roo.MessageBox} This message box
2394          */
2395         updateText : function(text){
2396             if(!dlg.isVisible() && !opt.width){
2397                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2398             }
2399             msgEl.innerHTML = text || '&#160;';
2400       
2401             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2402             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2403             var w = Math.max(
2404                     Math.min(opt.width || cw , this.maxWidth), 
2405                     Math.max(opt.minWidth || this.minWidth, bwidth)
2406             );
2407             if(opt.prompt){
2408                 activeTextEl.setWidth(w);
2409             }
2410             if(dlg.isVisible()){
2411                 dlg.fixedcenter = false;
2412             }
2413             // to big, make it scroll. = But as usual stupid IE does not support
2414             // !important..
2415             
2416             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2417                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2418                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2419             } else {
2420                 bodyEl.dom.style.height = '';
2421                 bodyEl.dom.style.overflowY = '';
2422             }
2423             if (cw > w) {
2424                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2425             } else {
2426                 bodyEl.dom.style.overflowX = '';
2427             }
2428             
2429             dlg.setContentSize(w, bodyEl.getHeight());
2430             if(dlg.isVisible()){
2431                 dlg.fixedcenter = true;
2432             }
2433             return this;
2434         },
2435
2436         /**
2437          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
2438          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2439          * @param {Number} value Any number between 0 and 1 (e.g., .5)
2440          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2441          * @return {Roo.MessageBox} This message box
2442          */
2443         updateProgress : function(value, text){
2444             if(text){
2445                 this.updateText(text);
2446             }
2447             if (pp) { // weird bug on my firefox - for some reason this is not defined
2448                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2449             }
2450             return this;
2451         },        
2452
2453         /**
2454          * Returns true if the message box is currently displayed
2455          * @return {Boolean} True if the message box is visible, else false
2456          */
2457         isVisible : function(){
2458             return dlg && dlg.isVisible();  
2459         },
2460
2461         /**
2462          * Hides the message box if it is displayed
2463          */
2464         hide : function(){
2465             if(this.isVisible()){
2466                 dlg.hide();
2467             }  
2468         },
2469
2470         /**
2471          * Displays a new message box, or reinitializes an existing message box, based on the config options
2472          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2473          * The following config object properties are supported:
2474          * <pre>
2475 Property    Type             Description
2476 ----------  ---------------  ------------------------------------------------------------------------------------
2477 animEl            String/Element   An id or Element from which the message box should animate as it opens and
2478                                    closes (defaults to undefined)
2479 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
2480                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
2481 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
2482                                    progress and wait dialogs will ignore this property and always hide the
2483                                    close button as they can only be closed programmatically.
2484 cls               String           A custom CSS class to apply to the message box element
2485 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
2486                                    displayed (defaults to 75)
2487 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
2488                                    function will be btn (the name of the button that was clicked, if applicable,
2489                                    e.g. "ok"), and text (the value of the active text field, if applicable).
2490                                    Progress and wait dialogs will ignore this option since they do not respond to
2491                                    user actions and can only be closed programmatically, so any required function
2492                                    should be called by the same code after it closes the dialog.
2493 icon              String           A CSS class that provides a background image to be used as an icon for
2494                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
2495 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
2496 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
2497 modal             Boolean          False to allow user interaction with the page while the message box is
2498                                    displayed (defaults to true)
2499 msg               String           A string that will replace the existing message box body text (defaults
2500                                    to the XHTML-compliant non-breaking space character '&#160;')
2501 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
2502 progress          Boolean          True to display a progress bar (defaults to false)
2503 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
2504 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
2505 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
2506 title             String           The title text
2507 value             String           The string value to set into the active textbox element if displayed
2508 wait              Boolean          True to display a progress bar (defaults to false)
2509 width             Number           The width of the dialog in pixels
2510 </pre>
2511          *
2512          * Example usage:
2513          * <pre><code>
2514 Roo.Msg.show({
2515    title: 'Address',
2516    msg: 'Please enter your address:',
2517    width: 300,
2518    buttons: Roo.MessageBox.OKCANCEL,
2519    multiline: true,
2520    fn: saveAddress,
2521    animEl: 'addAddressBtn'
2522 });
2523 </code></pre>
2524          * @param {Object} config Configuration options
2525          * @return {Roo.MessageBox} This message box
2526          */
2527         show : function(options)
2528         {
2529             
2530             // this causes nightmares if you show one dialog after another
2531             // especially on callbacks..
2532              
2533             if(this.isVisible()){
2534                 
2535                 this.hide();
2536                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
2537                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
2538                 Roo.log("New Dialog Message:" +  options.msg )
2539                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
2540                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
2541                 
2542             }
2543             var d = this.getDialog();
2544             opt = options;
2545             d.setTitle(opt.title || "&#160;");
2546             d.close.setDisplayed(opt.closable !== false);
2547             activeTextEl = textboxEl;
2548             opt.prompt = opt.prompt || (opt.multiline ? true : false);
2549             if(opt.prompt){
2550                 if(opt.multiline){
2551                     textboxEl.hide();
2552                     textareaEl.show();
2553                     textareaEl.setHeight(typeof opt.multiline == "number" ?
2554                         opt.multiline : this.defaultTextHeight);
2555                     activeTextEl = textareaEl;
2556                 }else{
2557                     textboxEl.show();
2558                     textareaEl.hide();
2559                 }
2560             }else{
2561                 textboxEl.hide();
2562                 textareaEl.hide();
2563             }
2564             progressEl.setDisplayed(opt.progress === true);
2565             this.updateProgress(0);
2566             activeTextEl.dom.value = opt.value || "";
2567             if(opt.prompt){
2568                 dlg.setDefaultButton(activeTextEl);
2569             }else{
2570                 var bs = opt.buttons;
2571                 var db = null;
2572                 if(bs && bs.ok){
2573                     db = buttons["ok"];
2574                 }else if(bs && bs.yes){
2575                     db = buttons["yes"];
2576                 }
2577                 dlg.setDefaultButton(db);
2578             }
2579             bwidth = updateButtons(opt.buttons);
2580             this.updateText(opt.msg);
2581             if(opt.cls){
2582                 d.el.addClass(opt.cls);
2583             }
2584             d.proxyDrag = opt.proxyDrag === true;
2585             d.modal = opt.modal !== false;
2586             d.mask = opt.modal !== false ? mask : false;
2587             if(!d.isVisible()){
2588                 // force it to the end of the z-index stack so it gets a cursor in FF
2589                 document.body.appendChild(dlg.el.dom);
2590                 d.animateTarget = null;
2591                 d.show(options.animEl);
2592             }
2593             return this;
2594         },
2595
2596         /**
2597          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
2598          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
2599          * and closing the message box when the process is complete.
2600          * @param {String} title The title bar text
2601          * @param {String} msg The message box body text
2602          * @return {Roo.MessageBox} This message box
2603          */
2604         progress : function(title, msg){
2605             this.show({
2606                 title : title,
2607                 msg : msg,
2608                 buttons: false,
2609                 progress:true,
2610                 closable:false,
2611                 minWidth: this.minProgressWidth,
2612                 modal : true
2613             });
2614             return this;
2615         },
2616
2617         /**
2618          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
2619          * If a callback function is passed it will be called after the user clicks the button, and the
2620          * id of the button that was clicked will be passed as the only parameter to the callback
2621          * (could also be the top-right close button).
2622          * @param {String} title The title bar text
2623          * @param {String} msg The message box body text
2624          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2625          * @param {Object} scope (optional) The scope of the callback function
2626          * @return {Roo.MessageBox} This message box
2627          */
2628         alert : function(title, msg, fn, scope){
2629             this.show({
2630                 title : title,
2631                 msg : msg,
2632                 buttons: this.OK,
2633                 fn: fn,
2634                 scope : scope,
2635                 modal : true
2636             });
2637             return this;
2638         },
2639
2640         /**
2641          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
2642          * interaction while waiting for a long-running process to complete that does not have defined intervals.
2643          * You are responsible for closing the message box when the process is complete.
2644          * @param {String} msg The message box body text
2645          * @param {String} title (optional) The title bar text
2646          * @return {Roo.MessageBox} This message box
2647          */
2648         wait : function(msg, title){
2649             this.show({
2650                 title : title,
2651                 msg : msg,
2652                 buttons: false,
2653                 closable:false,
2654                 progress:true,
2655                 modal:true,
2656                 width:300,
2657                 wait:true
2658             });
2659             waitTimer = Roo.TaskMgr.start({
2660                 run: function(i){
2661                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
2662                 },
2663                 interval: 1000
2664             });
2665             return this;
2666         },
2667
2668         /**
2669          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
2670          * If a callback function is passed it will be called after the user clicks either button, and the id of the
2671          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
2672          * @param {String} title The title bar text
2673          * @param {String} msg The message box body text
2674          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2675          * @param {Object} scope (optional) The scope of the callback function
2676          * @return {Roo.MessageBox} This message box
2677          */
2678         confirm : function(title, msg, fn, scope){
2679             this.show({
2680                 title : title,
2681                 msg : msg,
2682                 buttons: this.YESNO,
2683                 fn: fn,
2684                 scope : scope,
2685                 modal : true
2686             });
2687             return this;
2688         },
2689
2690         /**
2691          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
2692          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
2693          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
2694          * (could also be the top-right close button) and the text that was entered will be passed as the two
2695          * parameters to the callback.
2696          * @param {String} title The title bar text
2697          * @param {String} msg The message box body text
2698          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2699          * @param {Object} scope (optional) The scope of the callback function
2700          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
2701          * property, or the height in pixels to create the textbox (defaults to false / single-line)
2702          * @return {Roo.MessageBox} This message box
2703          */
2704         prompt : function(title, msg, fn, scope, multiline){
2705             this.show({
2706                 title : title,
2707                 msg : msg,
2708                 buttons: this.OKCANCEL,
2709                 fn: fn,
2710                 minWidth:250,
2711                 scope : scope,
2712                 prompt:true,
2713                 multiline: multiline,
2714                 modal : true
2715             });
2716             return this;
2717         },
2718
2719         /**
2720          * Button config that displays a single OK button
2721          * @type Object
2722          */
2723         OK : {ok:true},
2724         /**
2725          * Button config that displays Yes and No buttons
2726          * @type Object
2727          */
2728         YESNO : {yes:true, no:true},
2729         /**
2730          * Button config that displays OK and Cancel buttons
2731          * @type Object
2732          */
2733         OKCANCEL : {ok:true, cancel:true},
2734         /**
2735          * Button config that displays Yes, No and Cancel buttons
2736          * @type Object
2737          */
2738         YESNOCANCEL : {yes:true, no:true, cancel:true},
2739
2740         /**
2741          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
2742          * @type Number
2743          */
2744         defaultTextHeight : 75,
2745         /**
2746          * The maximum width in pixels of the message box (defaults to 600)
2747          * @type Number
2748          */
2749         maxWidth : 600,
2750         /**
2751          * The minimum width in pixels of the message box (defaults to 100)
2752          * @type Number
2753          */
2754         minWidth : 100,
2755         /**
2756          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
2757          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
2758          * @type Number
2759          */
2760         minProgressWidth : 250,
2761         /**
2762          * An object containing the default button text strings that can be overriden for localized language support.
2763          * Supported properties are: ok, cancel, yes and no.
2764          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
2765          * @type Object
2766          */
2767         buttonText : {
2768             ok : "OK",
2769             cancel : "Cancel",
2770             yes : "Yes",
2771             no : "No"
2772         }
2773     };
2774 }();
2775
2776 /**
2777  * Shorthand for {@link Roo.MessageBox}
2778  */
2779 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox 
2780 Roo.Msg = Roo.Msg || Roo.MessageBox;
2781 /*
2782  * - LGPL
2783  *
2784  * navbar
2785  * 
2786  */
2787
2788 /**
2789  * @class Roo.bootstrap.Navbar
2790  * @extends Roo.bootstrap.Component
2791  * Bootstrap Navbar class
2792
2793  * @constructor
2794  * Create a new Navbar
2795  * @param {Object} config The config object
2796  */
2797
2798
2799 Roo.bootstrap.Navbar = function(config){
2800     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
2801     
2802 };
2803
2804 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
2805     
2806     
2807    
2808     // private
2809     navItems : false,
2810     loadMask : false,
2811     
2812     
2813     getAutoCreate : function(){
2814         
2815         
2816         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
2817         
2818     },
2819     
2820     initEvents :function ()
2821     {
2822         //Roo.log(this.el.select('.navbar-toggle',true));
2823         this.el.select('.navbar-toggle',true).on('click', function() {
2824            // Roo.log('click');
2825             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
2826         }, this);
2827         
2828         var mark = {
2829             tag: "div",
2830             cls:"x-dlg-mask"
2831         }
2832         
2833         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
2834         
2835         var size = this.el.getSize();
2836         this.maskEl.setSize(size.width, size.height);
2837         this.maskEl.enableDisplayMode("block");
2838         this.maskEl.hide();
2839         
2840         if(this.loadMask){
2841             this.maskEl.show();
2842         }
2843     },
2844     
2845     
2846     getChildContainer : function()
2847     {
2848         if (this.el.select('.collapse').getCount()) {
2849             return this.el.select('.collapse',true).first();
2850         }
2851         
2852         return this.el;
2853     },
2854     
2855     mask : function()
2856     {
2857         this.maskEl.show();
2858     },
2859     
2860     unmask : function()
2861     {
2862         this.maskEl.hide();
2863     } 
2864     
2865     
2866     
2867     
2868 });
2869
2870
2871
2872  
2873
2874  /*
2875  * - LGPL
2876  *
2877  * navbar
2878  * 
2879  */
2880
2881 /**
2882  * @class Roo.bootstrap.NavSimplebar
2883  * @extends Roo.bootstrap.Navbar
2884  * Bootstrap Sidebar class
2885  *
2886  * @cfg {Boolean} inverse is inverted color
2887  * 
2888  * @cfg {String} type (nav | pills | tabs)
2889  * @cfg {Boolean} arrangement stacked | justified
2890  * @cfg {String} align (left | right) alignment
2891  * 
2892  * @cfg {Boolean} main (true|false) main nav bar? default false
2893  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
2894  * 
2895  * @cfg {String} tag (header|footer|nav|div) default is nav 
2896
2897  * 
2898  * 
2899  * 
2900  * @constructor
2901  * Create a new Sidebar
2902  * @param {Object} config The config object
2903  */
2904
2905
2906 Roo.bootstrap.NavSimplebar = function(config){
2907     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
2908 };
2909
2910 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
2911     
2912     inverse: false,
2913     
2914     type: false,
2915     arrangement: '',
2916     align : false,
2917     
2918     
2919     
2920     main : false,
2921     
2922     
2923     tag : false,
2924     
2925     
2926     getAutoCreate : function(){
2927         
2928         
2929         var cfg = {
2930             tag : this.tag || 'div',
2931             cls : 'navbar'
2932         };
2933           
2934         
2935         cfg.cn = [
2936             {
2937                 cls: 'nav',
2938                 tag : 'ul'
2939             }
2940         ];
2941         
2942          
2943         this.type = this.type || 'nav';
2944         if (['tabs','pills'].indexOf(this.type)!==-1) {
2945             cfg.cn[0].cls += ' nav-' + this.type
2946         
2947         
2948         } else {
2949             if (this.type!=='nav') {
2950                 Roo.log('nav type must be nav/tabs/pills')
2951             }
2952             cfg.cn[0].cls += ' navbar-nav'
2953         }
2954         
2955         
2956         
2957         
2958         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
2959             cfg.cn[0].cls += ' nav-' + this.arrangement;
2960         }
2961         
2962         
2963         if (this.align === 'right') {
2964             cfg.cn[0].cls += ' navbar-right';
2965         }
2966         
2967         if (this.inverse) {
2968             cfg.cls += ' navbar-inverse';
2969             
2970         }
2971         
2972         
2973         return cfg;
2974     
2975         
2976     }
2977     
2978     
2979     
2980 });
2981
2982
2983
2984  
2985
2986  
2987        /*
2988  * - LGPL
2989  *
2990  * navbar
2991  * 
2992  */
2993
2994 /**
2995  * @class Roo.bootstrap.NavHeaderbar
2996  * @extends Roo.bootstrap.NavSimplebar
2997  * Bootstrap Sidebar class
2998  *
2999  * @cfg {String} brand what is brand
3000  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3001  * @cfg {String} brand_href href of the brand
3002  * @cfg {Boolean} srButton generate the sr-only button (true | false) default true
3003  * 
3004  * @constructor
3005  * Create a new Sidebar
3006  * @param {Object} config The config object
3007  */
3008
3009
3010 Roo.bootstrap.NavHeaderbar = function(config){
3011     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3012 };
3013
3014 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3015     
3016     position: '',
3017     brand: '',
3018     brand_href: false,
3019     srButton : true,
3020     
3021     
3022     getAutoCreate : function(){
3023         
3024         var   cfg = {
3025             tag: this.nav || 'nav',
3026             cls: 'navbar',
3027             role: 'navigation',
3028             cn: []
3029         };
3030         
3031         if(this.srButton){
3032             cfg.cn.push({
3033                 tag: 'div',
3034                 cls: 'navbar-header',
3035                 cn: [
3036                     {
3037                         tag: 'button',
3038                         type: 'button',
3039                         cls: 'navbar-toggle',
3040                         'data-toggle': 'collapse',
3041                         cn: [
3042                             {
3043                                 tag: 'span',
3044                                 cls: 'sr-only',
3045                                 html: 'Toggle navigation'
3046                             },
3047                             {
3048                                 tag: 'span',
3049                                 cls: 'icon-bar'
3050                             },
3051                             {
3052                                 tag: 'span',
3053                                 cls: 'icon-bar'
3054                             },
3055                             {
3056                                 tag: 'span',
3057                                 cls: 'icon-bar'
3058                             }
3059                         ]
3060                     }
3061                 ]
3062             });
3063         }
3064         
3065         cfg.cn.push({
3066             tag: 'div',
3067             cls: 'collapse navbar-collapse',
3068             cn : []
3069         });
3070         
3071         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3072         
3073         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3074             cfg.cls += ' navbar-' + this.position;
3075             
3076             // tag can override this..
3077             
3078             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3079         }
3080         
3081         if (this.brand !== '') {
3082             cfg.cn[0].cn.push({
3083                 tag: 'a',
3084                 href: this.brand_href ? this.brand_href : '#',
3085                 cls: 'navbar-brand',
3086                 cn: [
3087                 this.brand
3088                 ]
3089             });
3090         }
3091         
3092         if(this.main){
3093             cfg.cls += ' main-nav';
3094         }
3095         
3096         
3097         return cfg;
3098
3099         
3100     }
3101     
3102     
3103     
3104 });
3105
3106
3107
3108  
3109
3110  /*
3111  * - LGPL
3112  *
3113  * navbar
3114  * 
3115  */
3116
3117 /**
3118  * @class Roo.bootstrap.NavSidebar
3119  * @extends Roo.bootstrap.Navbar
3120  * Bootstrap Sidebar class
3121  * 
3122  * @constructor
3123  * Create a new Sidebar
3124  * @param {Object} config The config object
3125  */
3126
3127
3128 Roo.bootstrap.NavSidebar = function(config){
3129     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3130 };
3131
3132 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3133     
3134     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3135     
3136     getAutoCreate : function(){
3137         
3138         
3139         return  {
3140             tag: 'div',
3141             cls: 'sidebar sidebar-nav'
3142         };
3143     
3144         
3145     }
3146     
3147     
3148     
3149 });
3150
3151
3152
3153  
3154
3155  /*
3156  * - LGPL
3157  *
3158  * nav group
3159  * 
3160  */
3161
3162 /**
3163  * @class Roo.bootstrap.NavGroup
3164  * @extends Roo.bootstrap.Component
3165  * Bootstrap NavGroup class
3166  * @cfg {String} align left | right
3167  * @cfg {Boolean} inverse false | true
3168  * @cfg {String} type (nav|pills|tab) default nav
3169  * @cfg {String} navId - reference Id for navbar.
3170
3171  * 
3172  * @constructor
3173  * Create a new nav group
3174  * @param {Object} config The config object
3175  */
3176
3177 Roo.bootstrap.NavGroup = function(config){
3178     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3179     this.navItems = [];
3180    
3181     Roo.bootstrap.NavGroup.register(this);
3182      this.addEvents({
3183         /**
3184              * @event changed
3185              * Fires when the active item changes
3186              * @param {Roo.bootstrap.NavGroup} this
3187              * @param {Roo.bootstrap.Navbar.Item} item The item selected
3188              * @param {Roo.bootstrap.Navbar.Item} item The previously selected item 
3189          */
3190         'changed': true
3191      });
3192     
3193 };
3194
3195 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3196     
3197     align: '',
3198     inverse: false,
3199     form: false,
3200     type: 'nav',
3201     navId : '',
3202     // private
3203     
3204     navItems : false, 
3205     
3206     getAutoCreate : function()
3207     {
3208         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3209         
3210         cfg = {
3211             tag : 'ul',
3212             cls: 'nav' 
3213         }
3214         
3215         if (['tabs','pills'].indexOf(this.type)!==-1) {
3216             cfg.cls += ' nav-' + this.type
3217         } else {
3218             if (this.type!=='nav') {
3219                 Roo.log('nav type must be nav/tabs/pills')
3220             }
3221             cfg.cls += ' navbar-nav'
3222         }
3223         
3224         if (this.parent().sidebar) {
3225             cfg = {
3226                 tag: 'ul',
3227                 cls: 'dashboard-menu sidebar-menu'
3228             }
3229             
3230             return cfg;
3231         }
3232         
3233         if (this.form === true) {
3234             cfg = {
3235                 tag: 'form',
3236                 cls: 'navbar-form'
3237             }
3238             
3239             if (this.align === 'right') {
3240                 cfg.cls += ' navbar-right';
3241             } else {
3242                 cfg.cls += ' navbar-left';
3243             }
3244         }
3245         
3246         if (this.align === 'right') {
3247             cfg.cls += ' navbar-right';
3248         }
3249         
3250         if (this.inverse) {
3251             cfg.cls += ' navbar-inverse';
3252             
3253         }
3254         
3255         
3256         return cfg;
3257     },
3258     /**
3259     * sets the active Navigation item
3260     * @param {Roo.bootstrap.NavItem} the new current navitem
3261     */
3262     setActiveItem : function(item)
3263     {
3264         var prev = false;
3265         Roo.each(this.navItems, function(v){
3266             if (v == item) {
3267                 return ;
3268             }
3269             if (v.isActive()) {
3270                 v.setActive(false, true);
3271                 prev = v;
3272                 
3273             }
3274             
3275         });
3276
3277         item.setActive(true, true);
3278         this.fireEvent('changed', this, item, prev);
3279         
3280         
3281     },
3282     /**
3283     * gets the active Navigation item
3284     * @return {Roo.bootstrap.NavItem} the current navitem
3285     */
3286     getActive : function()
3287     {
3288         
3289         var prev = false;
3290         Roo.each(this.navItems, function(v){
3291             
3292             if (v.isActive()) {
3293                 prev = v;
3294                 
3295             }
3296             
3297         });
3298         return prev;
3299     },
3300     /**
3301     * adds a Navigation item
3302     * @param {Roo.bootstrap.NavItem} the navitem to add
3303     */
3304     addItem : function(cfg)
3305     {
3306         var cn = new Roo.bootstrap.NavItem(cfg);
3307         this.register(cn);
3308         cn.parentId = this.id;
3309         cn.onRender(this.el, null);
3310         return cn;
3311     },
3312     /**
3313     * register a Navigation item
3314     * @param {Roo.bootstrap.NavItem} the navitem to add
3315     */
3316     register : function(item)
3317     {
3318         this.navItems.push( item);
3319         item.navId = this.navId;
3320     
3321     },
3322   
3323     
3324     getNavItem: function(tabId)
3325     {
3326         var ret = false;
3327         Roo.each(this.navItems, function(e) {
3328             if (e.tabId == tabId) {
3329                ret =  e;
3330                return false;
3331             }
3332             return true;
3333             
3334         });
3335         return ret;
3336     }
3337     
3338     
3339     
3340     
3341 });
3342
3343  
3344 Roo.apply(Roo.bootstrap.NavGroup, {
3345     
3346     groups: {},
3347      /**
3348     * register a Navigation Group
3349     * @param {Roo.bootstrap.NavGroup} the navgroup to add
3350     */
3351     register : function(navgrp)
3352     {
3353         this.groups[navgrp.navId] = navgrp;
3354         
3355     },
3356     /**
3357     * fetch a Navigation Group based on the navigation ID
3358     * @param {string} the navgroup to add
3359     * @returns {Roo.bootstrap.NavGroup} the navgroup 
3360     */
3361     get: function(navId) {
3362         if (typeof(this.groups[navId]) == 'undefined') {
3363             return false;
3364             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
3365         }
3366         return this.groups[navId] ;
3367     }
3368     
3369     
3370     
3371 });
3372
3373  /*
3374  * - LGPL
3375  *
3376  * row
3377  * 
3378  */
3379
3380 /**
3381  * @class Roo.bootstrap.NavItem
3382  * @extends Roo.bootstrap.Component
3383  * Bootstrap Navbar.NavItem class
3384  * @cfg {String} href  link to
3385  * @cfg {String} html content of button
3386  * @cfg {String} badge text inside badge
3387  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
3388  * @cfg {String} glyphicon name of glyphicon
3389  * @cfg {String} icon name of font awesome icon
3390  * @cfg {Boolean} active Is item active
3391  * @cfg {Boolean} disabled Is item disabled
3392  
3393  * @cfg {Boolean} preventDefault (true | false) default false
3394  * @cfg {String} tabId the tab that this item activates.
3395  * @cfg {String} tagtype (a|span) render as a href or span?
3396   
3397  * @constructor
3398  * Create a new Navbar Item
3399  * @param {Object} config The config object
3400  */
3401 Roo.bootstrap.NavItem = function(config){
3402     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
3403     this.addEvents({
3404         // raw events
3405         /**
3406          * @event click
3407          * The raw click event for the entire grid.
3408          * @param {Roo.EventObject} e
3409          */
3410         "click" : true,
3411          /**
3412             * @event changed
3413             * Fires when the active item active state changes
3414             * @param {Roo.bootstrap.NavItem} this
3415             * @param {boolean} state the new state
3416              
3417          */
3418         'changed': true
3419     });
3420    
3421 };
3422
3423 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
3424     
3425     href: false,
3426     html: '',
3427     badge: '',
3428     icon: false,
3429     glyphicon: false,
3430     active: false,
3431     preventDefault : false,
3432     tabId : false,
3433     tagtype : 'a',
3434     disabled : false,
3435     
3436     getAutoCreate : function(){
3437          
3438         var cfg = {
3439             tag: 'li',
3440             cls: 'nav-item'
3441             
3442         }
3443         if (this.active) {
3444             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
3445         }
3446         if (this.disabled) {
3447             cfg.cls += ' disabled';
3448         }
3449         
3450         if (this.href || this.html || this.glyphicon || this.icon) {
3451             cfg.cn = [
3452                 {
3453                     tag: this.tagtype,
3454                     href : this.href || "#",
3455                     html: this.html || ''
3456                 }
3457             ];
3458             
3459             if (this.icon) {
3460                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
3461             }
3462
3463             if(this.glyphicon) {
3464                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
3465             }
3466             
3467             if (this.menu) {
3468                 
3469                 cfg.cn[0].html += " <span class='caret'></span>";
3470              
3471             }
3472             
3473             if (this.badge !== '') {
3474                  
3475                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
3476             }
3477         }
3478         
3479         
3480         
3481         return cfg;
3482     },
3483     initEvents: function() {
3484        // Roo.log('init events?');
3485        // Roo.log(this.el.dom);
3486         if (typeof (this.menu) != 'undefined') {
3487             this.menu.parentType = this.xtype;
3488             this.menu.triggerEl = this.el;
3489             this.addxtype(Roo.apply({}, this.menu));
3490         }
3491
3492        
3493         this.el.select('a',true).on('click', this.onClick, this);
3494         // at this point parent should be available..
3495         this.parent().register(this);
3496     },
3497     
3498     onClick : function(e)
3499     {
3500          
3501         if(this.preventDefault){
3502             e.preventDefault();
3503         }
3504         if (this.disabled) {
3505             return;
3506         }
3507         Roo.log("fire event clicked");
3508         if(this.fireEvent('click', this, e) === false){
3509             return;
3510         };
3511         
3512         if (['tabs','pills'].indexOf(this.parent().type)!==-1) {
3513             if (typeof(this.parent().setActiveItem) !== 'undefined') {
3514                 this.parent().setActiveItem(this);
3515             }
3516             
3517             
3518             
3519         } 
3520     },
3521     
3522     isActive: function () {
3523         return this.active
3524     },
3525     setActive : function(state, fire)
3526     {
3527         this.active = state;
3528         if (!state ) {
3529             this.el.removeClass('active');
3530         } else if (!this.el.hasClass('active')) {
3531             this.el.addClass('active');
3532         }
3533         if (fire) {
3534             this.fireEvent('changed', this, state);
3535         }
3536         
3537         
3538     },
3539      // this should not be here...
3540     setDisabled : function(state)
3541     {
3542         this.disabled = state;
3543         if (!state ) {
3544             this.el.removeClass('disabled');
3545         } else if (!this.el.hasClass('disabled')) {
3546             this.el.addClass('disabled');
3547         }
3548         
3549     }
3550 });
3551  
3552
3553  /*
3554  * - LGPL
3555  *
3556  * sidebar item
3557  *
3558  *  li
3559  *    <span> icon </span>
3560  *    <span> text </span>
3561  *    <span>badge </span>
3562  */
3563
3564 /**
3565  * @class Roo.bootstrap.NavSidebarItem
3566  * @extends Roo.bootstrap.NavItem
3567  * Bootstrap Navbar.NavSidebarItem class
3568  * @constructor
3569  * Create a new Navbar Button
3570  * @param {Object} config The config object
3571  */
3572 Roo.bootstrap.NavSidebarItem = function(config){
3573     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
3574     this.addEvents({
3575         // raw events
3576         /**
3577          * @event click
3578          * The raw click event for the entire grid.
3579          * @param {Roo.EventObject} e
3580          */
3581         "click" : true,
3582          /**
3583             * @event changed
3584             * Fires when the active item active state changes
3585             * @param {Roo.bootstrap.NavSidebarItem} this
3586             * @param {boolean} state the new state
3587              
3588          */
3589         'changed': true
3590     });
3591    
3592 };
3593
3594 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
3595     
3596     
3597     getAutoCreate : function(){
3598         
3599         
3600         var a = {
3601                 tag: 'a',
3602                 href : this.href || '#',
3603                 cls: '',
3604                 html : '',
3605                 cn : []
3606         };
3607         var cfg = {
3608             tag: 'li',
3609             cls: '',
3610             cn: [ a ]
3611         }
3612         var span = {
3613             tag: 'span',
3614             html : this.html || ''
3615         }
3616         
3617         
3618         if (this.active) {
3619             cfg.cls += ' active';
3620         }
3621         
3622         // left icon..
3623         if (this.glyphicon || this.icon) {
3624             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
3625             a.cn.push({ tag : 'i', cls : c }) ;
3626         }
3627         // html..
3628         a.cn.push(span);
3629         // then badge..
3630         if (this.badge !== '') {
3631             a.cn.push({ tag: 'span',  cls : 'badge pull-right ' + (this.badgecls || ''), html: this.badge }); 
3632         }
3633         // fi
3634         if (this.menu) {
3635             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
3636             a.cls += 'dropdown-toggle treeview' ;
3637             
3638         }
3639         
3640         
3641         
3642         return cfg;
3643          
3644            
3645     }
3646    
3647      
3648  
3649 });
3650  
3651
3652  /*
3653  * - LGPL
3654  *
3655  * row
3656  * 
3657  */
3658
3659 /**
3660  * @class Roo.bootstrap.Row
3661  * @extends Roo.bootstrap.Component
3662  * Bootstrap Row class (contains columns...)
3663  * 
3664  * @constructor
3665  * Create a new Row
3666  * @param {Object} config The config object
3667  */
3668
3669 Roo.bootstrap.Row = function(config){
3670     Roo.bootstrap.Row.superclass.constructor.call(this, config);
3671 };
3672
3673 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
3674     
3675     getAutoCreate : function(){
3676        return {
3677             cls: 'row clearfix'
3678        };
3679     }
3680     
3681     
3682 });
3683
3684  
3685
3686  /*
3687  * - LGPL
3688  *
3689  * element
3690  * 
3691  */
3692
3693 /**
3694  * @class Roo.bootstrap.Element
3695  * @extends Roo.bootstrap.Component
3696  * Bootstrap Element class
3697  * @cfg {String} html contents of the element
3698  * @cfg {String} tag tag of the element
3699  * @cfg {String} cls class of the element
3700  * 
3701  * @constructor
3702  * Create a new Element
3703  * @param {Object} config The config object
3704  */
3705
3706 Roo.bootstrap.Element = function(config){
3707     Roo.bootstrap.Element.superclass.constructor.call(this, config);
3708 };
3709
3710 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
3711     
3712     tag: 'div',
3713     cls: '',
3714     html: '',
3715      
3716     
3717     getAutoCreate : function(){
3718         
3719         var cfg = {
3720             tag: this.tag,
3721             cls: this.cls,
3722             html: this.html
3723         }
3724         
3725         
3726         
3727         return cfg;
3728     }
3729    
3730 });
3731
3732  
3733
3734  /*
3735  * - LGPL
3736  *
3737  * pagination
3738  * 
3739  */
3740
3741 /**
3742  * @class Roo.bootstrap.Pagination
3743  * @extends Roo.bootstrap.Component
3744  * Bootstrap Pagination class
3745  * @cfg {String} size xs | sm | md | lg
3746  * @cfg {Boolean} inverse false | true
3747  * 
3748  * @constructor
3749  * Create a new Pagination
3750  * @param {Object} config The config object
3751  */
3752
3753 Roo.bootstrap.Pagination = function(config){
3754     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
3755 };
3756
3757 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
3758     
3759     cls: false,
3760     size: false,
3761     inverse: false,
3762     
3763     getAutoCreate : function(){
3764         var cfg = {
3765             tag: 'ul',
3766                 cls: 'pagination'
3767         };
3768         if (this.inverse) {
3769             cfg.cls += ' inverse';
3770         }
3771         if (this.html) {
3772             cfg.html=this.html;
3773         }
3774         if (this.cls) {
3775             cfg.cls += " " + this.cls;
3776         }
3777         return cfg;
3778     }
3779    
3780 });
3781
3782  
3783
3784  /*
3785  * - LGPL
3786  *
3787  * Pagination item
3788  * 
3789  */
3790
3791
3792 /**
3793  * @class Roo.bootstrap.PaginationItem
3794  * @extends Roo.bootstrap.Component
3795  * Bootstrap PaginationItem class
3796  * @cfg {String} html text
3797  * @cfg {String} href the link
3798  * @cfg {Boolean} preventDefault (true | false) default true
3799  * @cfg {Boolean} active (true | false) default false
3800  * 
3801  * 
3802  * @constructor
3803  * Create a new PaginationItem
3804  * @param {Object} config The config object
3805  */
3806
3807
3808 Roo.bootstrap.PaginationItem = function(config){
3809     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
3810     this.addEvents({
3811         // raw events
3812         /**
3813          * @event click
3814          * The raw click event for the entire grid.
3815          * @param {Roo.EventObject} e
3816          */
3817         "click" : true
3818     });
3819 };
3820
3821 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
3822     
3823     href : false,
3824     html : false,
3825     preventDefault: true,
3826     active : false,
3827     cls : false,
3828     
3829     getAutoCreate : function(){
3830         var cfg= {
3831             tag: 'li',
3832             cn: [
3833                 {
3834                     tag : 'a',
3835                     href : this.href ? this.href : '#',
3836                     html : this.html ? this.html : ''
3837                 }
3838             ]
3839         };
3840         
3841         if(this.cls){
3842             cfg.cls = this.cls;
3843         }
3844         
3845         if(this.active){
3846             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
3847         }
3848         
3849         return cfg;
3850     },
3851     
3852     initEvents: function() {
3853         
3854         this.el.on('click', this.onClick, this);
3855         
3856     },
3857     onClick : function(e)
3858     {
3859         Roo.log('PaginationItem on click ');
3860         if(this.preventDefault){
3861             e.preventDefault();
3862         }
3863         
3864         this.fireEvent('click', this, e);
3865     }
3866    
3867 });
3868
3869  
3870
3871  /*
3872  * - LGPL
3873  *
3874  * slider
3875  * 
3876  */
3877
3878
3879 /**
3880  * @class Roo.bootstrap.Slider
3881  * @extends Roo.bootstrap.Component
3882  * Bootstrap Slider class
3883  *    
3884  * @constructor
3885  * Create a new Slider
3886  * @param {Object} config The config object
3887  */
3888
3889 Roo.bootstrap.Slider = function(config){
3890     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
3891 };
3892
3893 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
3894     
3895     getAutoCreate : function(){
3896         
3897         var cfg = {
3898             tag: 'div',
3899             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
3900             cn: [
3901                 {
3902                     tag: 'a',
3903                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
3904                 }
3905             ]
3906         }
3907         
3908         return cfg;
3909     }
3910    
3911 });
3912
3913  /*
3914  * Based on:
3915  * Ext JS Library 1.1.1
3916  * Copyright(c) 2006-2007, Ext JS, LLC.
3917  *
3918  * Originally Released Under LGPL - original licence link has changed is not relivant.
3919  *
3920  * Fork - LGPL
3921  * <script type="text/javascript">
3922  */
3923  
3924
3925 /**
3926  * @class Roo.grid.ColumnModel
3927  * @extends Roo.util.Observable
3928  * This is the default implementation of a ColumnModel used by the Grid. It defines
3929  * the columns in the grid.
3930  * <br>Usage:<br>
3931  <pre><code>
3932  var colModel = new Roo.grid.ColumnModel([
3933         {header: "Ticker", width: 60, sortable: true, locked: true},
3934         {header: "Company Name", width: 150, sortable: true},
3935         {header: "Market Cap.", width: 100, sortable: true},
3936         {header: "$ Sales", width: 100, sortable: true, renderer: money},
3937         {header: "Employees", width: 100, sortable: true, resizable: false}
3938  ]);
3939  </code></pre>
3940  * <p>
3941  
3942  * The config options listed for this class are options which may appear in each
3943  * individual column definition.
3944  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
3945  * @constructor
3946  * @param {Object} config An Array of column config objects. See this class's
3947  * config objects for details.
3948 */
3949 Roo.grid.ColumnModel = function(config){
3950         /**
3951      * The config passed into the constructor
3952      */
3953     this.config = config;
3954     this.lookup = {};
3955
3956     // if no id, create one
3957     // if the column does not have a dataIndex mapping,
3958     // map it to the order it is in the config
3959     for(var i = 0, len = config.length; i < len; i++){
3960         var c = config[i];
3961         if(typeof c.dataIndex == "undefined"){
3962             c.dataIndex = i;
3963         }
3964         if(typeof c.renderer == "string"){
3965             c.renderer = Roo.util.Format[c.renderer];
3966         }
3967         if(typeof c.id == "undefined"){
3968             c.id = Roo.id();
3969         }
3970         if(c.editor && c.editor.xtype){
3971             c.editor  = Roo.factory(c.editor, Roo.grid);
3972         }
3973         if(c.editor && c.editor.isFormField){
3974             c.editor = new Roo.grid.GridEditor(c.editor);
3975         }
3976         this.lookup[c.id] = c;
3977     }
3978
3979     /**
3980      * The width of columns which have no width specified (defaults to 100)
3981      * @type Number
3982      */
3983     this.defaultWidth = 100;
3984
3985     /**
3986      * Default sortable of columns which have no sortable specified (defaults to false)
3987      * @type Boolean
3988      */
3989     this.defaultSortable = false;
3990
3991     this.addEvents({
3992         /**
3993              * @event widthchange
3994              * Fires when the width of a column changes.
3995              * @param {ColumnModel} this
3996              * @param {Number} columnIndex The column index
3997              * @param {Number} newWidth The new width
3998              */
3999             "widthchange": true,
4000         /**
4001              * @event headerchange
4002              * Fires when the text of a header changes.
4003              * @param {ColumnModel} this
4004              * @param {Number} columnIndex The column index
4005              * @param {Number} newText The new header text
4006              */
4007             "headerchange": true,
4008         /**
4009              * @event hiddenchange
4010              * Fires when a column is hidden or "unhidden".
4011              * @param {ColumnModel} this
4012              * @param {Number} columnIndex The column index
4013              * @param {Boolean} hidden true if hidden, false otherwise
4014              */
4015             "hiddenchange": true,
4016             /**
4017          * @event columnmoved
4018          * Fires when a column is moved.
4019          * @param {ColumnModel} this
4020          * @param {Number} oldIndex
4021          * @param {Number} newIndex
4022          */
4023         "columnmoved" : true,
4024         /**
4025          * @event columlockchange
4026          * Fires when a column's locked state is changed
4027          * @param {ColumnModel} this
4028          * @param {Number} colIndex
4029          * @param {Boolean} locked true if locked
4030          */
4031         "columnlockchange" : true
4032     });
4033     Roo.grid.ColumnModel.superclass.constructor.call(this);
4034 };
4035 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4036     /**
4037      * @cfg {String} header The header text to display in the Grid view.
4038      */
4039     /**
4040      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4041      * {@link Roo.data.Record} definition from which to draw the column's value. If not
4042      * specified, the column's index is used as an index into the Record's data Array.
4043      */
4044     /**
4045      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4046      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4047      */
4048     /**
4049      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4050      * Defaults to the value of the {@link #defaultSortable} property.
4051      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4052      */
4053     /**
4054      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
4055      */
4056     /**
4057      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
4058      */
4059     /**
4060      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4061      */
4062     /**
4063      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4064      */
4065     /**
4066      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4067      * given the cell's data value. See {@link #setRenderer}. If not specified, the
4068      * default renderer uses the raw data value. If an object is returned (bootstrap only)
4069      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4070      */
4071        /**
4072      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
4073      */
4074     /**
4075      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
4076      */
4077
4078     /**
4079      * Returns the id of the column at the specified index.
4080      * @param {Number} index The column index
4081      * @return {String} the id
4082      */
4083     getColumnId : function(index){
4084         return this.config[index].id;
4085     },
4086
4087     /**
4088      * Returns the column for a specified id.
4089      * @param {String} id The column id
4090      * @return {Object} the column
4091      */
4092     getColumnById : function(id){
4093         return this.lookup[id];
4094     },
4095
4096     
4097     /**
4098      * Returns the column for a specified dataIndex.
4099      * @param {String} dataIndex The column dataIndex
4100      * @return {Object|Boolean} the column or false if not found
4101      */
4102     getColumnByDataIndex: function(dataIndex){
4103         var index = this.findColumnIndex(dataIndex);
4104         return index > -1 ? this.config[index] : false;
4105     },
4106     
4107     /**
4108      * Returns the index for a specified column id.
4109      * @param {String} id The column id
4110      * @return {Number} the index, or -1 if not found
4111      */
4112     getIndexById : function(id){
4113         for(var i = 0, len = this.config.length; i < len; i++){
4114             if(this.config[i].id == id){
4115                 return i;
4116             }
4117         }
4118         return -1;
4119     },
4120     
4121     /**
4122      * Returns the index for a specified column dataIndex.
4123      * @param {String} dataIndex The column dataIndex
4124      * @return {Number} the index, or -1 if not found
4125      */
4126     
4127     findColumnIndex : function(dataIndex){
4128         for(var i = 0, len = this.config.length; i < len; i++){
4129             if(this.config[i].dataIndex == dataIndex){
4130                 return i;
4131             }
4132         }
4133         return -1;
4134     },
4135     
4136     
4137     moveColumn : function(oldIndex, newIndex){
4138         var c = this.config[oldIndex];
4139         this.config.splice(oldIndex, 1);
4140         this.config.splice(newIndex, 0, c);
4141         this.dataMap = null;
4142         this.fireEvent("columnmoved", this, oldIndex, newIndex);
4143     },
4144
4145     isLocked : function(colIndex){
4146         return this.config[colIndex].locked === true;
4147     },
4148
4149     setLocked : function(colIndex, value, suppressEvent){
4150         if(this.isLocked(colIndex) == value){
4151             return;
4152         }
4153         this.config[colIndex].locked = value;
4154         if(!suppressEvent){
4155             this.fireEvent("columnlockchange", this, colIndex, value);
4156         }
4157     },
4158
4159     getTotalLockedWidth : function(){
4160         var totalWidth = 0;
4161         for(var i = 0; i < this.config.length; i++){
4162             if(this.isLocked(i) && !this.isHidden(i)){
4163                 this.totalWidth += this.getColumnWidth(i);
4164             }
4165         }
4166         return totalWidth;
4167     },
4168
4169     getLockedCount : function(){
4170         for(var i = 0, len = this.config.length; i < len; i++){
4171             if(!this.isLocked(i)){
4172                 return i;
4173             }
4174         }
4175     },
4176
4177     /**
4178      * Returns the number of columns.
4179      * @return {Number}
4180      */
4181     getColumnCount : function(visibleOnly){
4182         if(visibleOnly === true){
4183             var c = 0;
4184             for(var i = 0, len = this.config.length; i < len; i++){
4185                 if(!this.isHidden(i)){
4186                     c++;
4187                 }
4188             }
4189             return c;
4190         }
4191         return this.config.length;
4192     },
4193
4194     /**
4195      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
4196      * @param {Function} fn
4197      * @param {Object} scope (optional)
4198      * @return {Array} result
4199      */
4200     getColumnsBy : function(fn, scope){
4201         var r = [];
4202         for(var i = 0, len = this.config.length; i < len; i++){
4203             var c = this.config[i];
4204             if(fn.call(scope||this, c, i) === true){
4205                 r[r.length] = c;
4206             }
4207         }
4208         return r;
4209     },
4210
4211     /**
4212      * Returns true if the specified column is sortable.
4213      * @param {Number} col The column index
4214      * @return {Boolean}
4215      */
4216     isSortable : function(col){
4217         if(typeof this.config[col].sortable == "undefined"){
4218             return this.defaultSortable;
4219         }
4220         return this.config[col].sortable;
4221     },
4222
4223     /**
4224      * Returns the rendering (formatting) function defined for the column.
4225      * @param {Number} col The column index.
4226      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
4227      */
4228     getRenderer : function(col){
4229         if(!this.config[col].renderer){
4230             return Roo.grid.ColumnModel.defaultRenderer;
4231         }
4232         return this.config[col].renderer;
4233     },
4234
4235     /**
4236      * Sets the rendering (formatting) function for a column.
4237      * @param {Number} col The column index
4238      * @param {Function} fn The function to use to process the cell's raw data
4239      * to return HTML markup for the grid view. The render function is called with
4240      * the following parameters:<ul>
4241      * <li>Data value.</li>
4242      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
4243      * <li>css A CSS style string to apply to the table cell.</li>
4244      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
4245      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
4246      * <li>Row index</li>
4247      * <li>Column index</li>
4248      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
4249      */
4250     setRenderer : function(col, fn){
4251         this.config[col].renderer = fn;
4252     },
4253
4254     /**
4255      * Returns the width for the specified column.
4256      * @param {Number} col The column index
4257      * @return {Number}
4258      */
4259     getColumnWidth : function(col){
4260         return this.config[col].width * 1 || this.defaultWidth;
4261     },
4262
4263     /**
4264      * Sets the width for a column.
4265      * @param {Number} col The column index
4266      * @param {Number} width The new width
4267      */
4268     setColumnWidth : function(col, width, suppressEvent){
4269         this.config[col].width = width;
4270         this.totalWidth = null;
4271         if(!suppressEvent){
4272              this.fireEvent("widthchange", this, col, width);
4273         }
4274     },
4275
4276     /**
4277      * Returns the total width of all columns.
4278      * @param {Boolean} includeHidden True to include hidden column widths
4279      * @return {Number}
4280      */
4281     getTotalWidth : function(includeHidden){
4282         if(!this.totalWidth){
4283             this.totalWidth = 0;
4284             for(var i = 0, len = this.config.length; i < len; i++){
4285                 if(includeHidden || !this.isHidden(i)){
4286                     this.totalWidth += this.getColumnWidth(i);
4287                 }
4288             }
4289         }
4290         return this.totalWidth;
4291     },
4292
4293     /**
4294      * Returns the header for the specified column.
4295      * @param {Number} col The column index
4296      * @return {String}
4297      */
4298     getColumnHeader : function(col){
4299         return this.config[col].header;
4300     },
4301
4302     /**
4303      * Sets the header for a column.
4304      * @param {Number} col The column index
4305      * @param {String} header The new header
4306      */
4307     setColumnHeader : function(col, header){
4308         this.config[col].header = header;
4309         this.fireEvent("headerchange", this, col, header);
4310     },
4311
4312     /**
4313      * Returns the tooltip for the specified column.
4314      * @param {Number} col The column index
4315      * @return {String}
4316      */
4317     getColumnTooltip : function(col){
4318             return this.config[col].tooltip;
4319     },
4320     /**
4321      * Sets the tooltip for a column.
4322      * @param {Number} col The column index
4323      * @param {String} tooltip The new tooltip
4324      */
4325     setColumnTooltip : function(col, tooltip){
4326             this.config[col].tooltip = tooltip;
4327     },
4328
4329     /**
4330      * Returns the dataIndex for the specified column.
4331      * @param {Number} col The column index
4332      * @return {Number}
4333      */
4334     getDataIndex : function(col){
4335         return this.config[col].dataIndex;
4336     },
4337
4338     /**
4339      * Sets the dataIndex for a column.
4340      * @param {Number} col The column index
4341      * @param {Number} dataIndex The new dataIndex
4342      */
4343     setDataIndex : function(col, dataIndex){
4344         this.config[col].dataIndex = dataIndex;
4345     },
4346
4347     
4348     
4349     /**
4350      * Returns true if the cell is editable.
4351      * @param {Number} colIndex The column index
4352      * @param {Number} rowIndex The row index
4353      * @return {Boolean}
4354      */
4355     isCellEditable : function(colIndex, rowIndex){
4356         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
4357     },
4358
4359     /**
4360      * Returns the editor defined for the cell/column.
4361      * return false or null to disable editing.
4362      * @param {Number} colIndex The column index
4363      * @param {Number} rowIndex The row index
4364      * @return {Object}
4365      */
4366     getCellEditor : function(colIndex, rowIndex){
4367         return this.config[colIndex].editor;
4368     },
4369
4370     /**
4371      * Sets if a column is editable.
4372      * @param {Number} col The column index
4373      * @param {Boolean} editable True if the column is editable
4374      */
4375     setEditable : function(col, editable){
4376         this.config[col].editable = editable;
4377     },
4378
4379
4380     /**
4381      * Returns true if the column is hidden.
4382      * @param {Number} colIndex The column index
4383      * @return {Boolean}
4384      */
4385     isHidden : function(colIndex){
4386         return this.config[colIndex].hidden;
4387     },
4388
4389
4390     /**
4391      * Returns true if the column width cannot be changed
4392      */
4393     isFixed : function(colIndex){
4394         return this.config[colIndex].fixed;
4395     },
4396
4397     /**
4398      * Returns true if the column can be resized
4399      * @return {Boolean}
4400      */
4401     isResizable : function(colIndex){
4402         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
4403     },
4404     /**
4405      * Sets if a column is hidden.
4406      * @param {Number} colIndex The column index
4407      * @param {Boolean} hidden True if the column is hidden
4408      */
4409     setHidden : function(colIndex, hidden){
4410         this.config[colIndex].hidden = hidden;
4411         this.totalWidth = null;
4412         this.fireEvent("hiddenchange", this, colIndex, hidden);
4413     },
4414
4415     /**
4416      * Sets the editor for a column.
4417      * @param {Number} col The column index
4418      * @param {Object} editor The editor object
4419      */
4420     setEditor : function(col, editor){
4421         this.config[col].editor = editor;
4422     }
4423 });
4424
4425 Roo.grid.ColumnModel.defaultRenderer = function(value){
4426         if(typeof value == "string" && value.length < 1){
4427             return "&#160;";
4428         }
4429         return value;
4430 };
4431
4432 // Alias for backwards compatibility
4433 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
4434 /*
4435  * Based on:
4436  * Ext JS Library 1.1.1
4437  * Copyright(c) 2006-2007, Ext JS, LLC.
4438  *
4439  * Originally Released Under LGPL - original licence link has changed is not relivant.
4440  *
4441  * Fork - LGPL
4442  * <script type="text/javascript">
4443  */
4444  
4445 /**
4446  * @class Roo.LoadMask
4447  * A simple utility class for generically masking elements while loading data.  If the element being masked has
4448  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
4449  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
4450  * element's UpdateManager load indicator and will be destroyed after the initial load.
4451  * @constructor
4452  * Create a new LoadMask
4453  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
4454  * @param {Object} config The config object
4455  */
4456 Roo.LoadMask = function(el, config){
4457     this.el = Roo.get(el);
4458     Roo.apply(this, config);
4459     if(this.store){
4460         this.store.on('beforeload', this.onBeforeLoad, this);
4461         this.store.on('load', this.onLoad, this);
4462         this.store.on('loadexception', this.onLoadException, this);
4463         this.removeMask = false;
4464     }else{
4465         var um = this.el.getUpdateManager();
4466         um.showLoadIndicator = false; // disable the default indicator
4467         um.on('beforeupdate', this.onBeforeLoad, this);
4468         um.on('update', this.onLoad, this);
4469         um.on('failure', this.onLoad, this);
4470         this.removeMask = true;
4471     }
4472 };
4473
4474 Roo.LoadMask.prototype = {
4475     /**
4476      * @cfg {Boolean} removeMask
4477      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
4478      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
4479      */
4480     /**
4481      * @cfg {String} msg
4482      * The text to display in a centered loading message box (defaults to 'Loading...')
4483      */
4484     msg : 'Loading...',
4485     /**
4486      * @cfg {String} msgCls
4487      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
4488      */
4489     msgCls : 'x-mask-loading',
4490
4491     /**
4492      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
4493      * @type Boolean
4494      */
4495     disabled: false,
4496
4497     /**
4498      * Disables the mask to prevent it from being displayed
4499      */
4500     disable : function(){
4501        this.disabled = true;
4502     },
4503
4504     /**
4505      * Enables the mask so that it can be displayed
4506      */
4507     enable : function(){
4508         this.disabled = false;
4509     },
4510     
4511     onLoadException : function()
4512     {
4513         Roo.log(arguments);
4514         
4515         if (typeof(arguments[3]) != 'undefined') {
4516             Roo.MessageBox.alert("Error loading",arguments[3]);
4517         } 
4518         /*
4519         try {
4520             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
4521                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
4522             }   
4523         } catch(e) {
4524             
4525         }
4526         */
4527     
4528         
4529         
4530         this.el.unmask(this.removeMask);
4531     },
4532     // private
4533     onLoad : function()
4534     {
4535         this.el.unmask(this.removeMask);
4536     },
4537
4538     // private
4539     onBeforeLoad : function(){
4540         if(!this.disabled){
4541             this.el.mask(this.msg, this.msgCls);
4542         }
4543     },
4544
4545     // private
4546     destroy : function(){
4547         if(this.store){
4548             this.store.un('beforeload', this.onBeforeLoad, this);
4549             this.store.un('load', this.onLoad, this);
4550             this.store.un('loadexception', this.onLoadException, this);
4551         }else{
4552             var um = this.el.getUpdateManager();
4553             um.un('beforeupdate', this.onBeforeLoad, this);
4554             um.un('update', this.onLoad, this);
4555             um.un('failure', this.onLoad, this);
4556         }
4557     }
4558 };/*
4559  * - LGPL
4560  *
4561  * table
4562  * 
4563  */
4564
4565 /**
4566  * @class Roo.bootstrap.Table
4567  * @extends Roo.bootstrap.Component
4568  * Bootstrap Table class
4569  * @cfg {String} cls table class
4570  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
4571  * @cfg {String} bgcolor Specifies the background color for a table
4572  * @cfg {Number} border Specifies whether the table cells should have borders or not
4573  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
4574  * @cfg {Number} cellspacing Specifies the space between cells
4575  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
4576  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
4577  * @cfg {String} sortable Specifies that the table should be sortable
4578  * @cfg {String} summary Specifies a summary of the content of a table
4579  * @cfg {Number} width Specifies the width of a table
4580  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
4581  * 
4582  * @cfg {boolean} striped Should the rows be alternative striped
4583  * @cfg {boolean} bordered Add borders to the table
4584  * @cfg {boolean} hover Add hover highlighting
4585  * @cfg {boolean} condensed Format condensed
4586  * @cfg {boolean} responsive Format condensed
4587  * @cfg {Boolean} loadMask (true|false) default false
4588  * @cfg {Boolean} tfoot (true|false) generate tfoot, default true
4589  * @cfg {Boolean} thead (true|false) generate thead, default true
4590  * @cfg {Boolean} RowSelection (true|false) default false
4591  * @cfg {Boolean} CellSelection (true|false) default false
4592  *
4593  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
4594  
4595  * 
4596  * @constructor
4597  * Create a new Table
4598  * @param {Object} config The config object
4599  */
4600
4601 Roo.bootstrap.Table = function(config){
4602     Roo.bootstrap.Table.superclass.constructor.call(this, config);
4603     
4604     if (this.sm) {
4605         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
4606         this.sm = this.selModel;
4607         this.sm.xmodule = this.xmodule || false;
4608     }
4609     if (this.cm && typeof(this.cm.config) == 'undefined') {
4610         this.colModel = new Roo.grid.ColumnModel(this.cm);
4611         this.cm = this.colModel;
4612         this.cm.xmodule = this.xmodule || false;
4613     }
4614     if (this.store) {
4615         this.store= Roo.factory(this.store, Roo.data);
4616         this.ds = this.store;
4617         this.ds.xmodule = this.xmodule || false;
4618          
4619     }
4620     if (this.footer && this.store) {
4621         this.footer.dataSource = this.ds;
4622         this.footer = Roo.factory(this.footer);
4623     }
4624     
4625     /** @private */
4626     this.addEvents({
4627         /**
4628          * @event cellclick
4629          * Fires when a cell is clicked
4630          * @param {Roo.bootstrap.Table} this
4631          * @param {Roo.Element} el
4632          * @param {Number} rowIndex
4633          * @param {Number} columnIndex
4634          * @param {Roo.EventObject} e
4635          */
4636         "cellclick" : true,
4637         /**
4638          * @event celldblclick
4639          * Fires when a cell is double clicked
4640          * @param {Roo.bootstrap.Table} this
4641          * @param {Roo.Element} el
4642          * @param {Number} rowIndex
4643          * @param {Number} columnIndex
4644          * @param {Roo.EventObject} e
4645          */
4646         "celldblclick" : true,
4647         /**
4648          * @event rowclick
4649          * Fires when a row is clicked
4650          * @param {Roo.bootstrap.Table} this
4651          * @param {Roo.Element} el
4652          * @param {Number} rowIndex
4653          * @param {Roo.EventObject} e
4654          */
4655         "rowclick" : true,
4656         /**
4657          * @event rowdblclick
4658          * Fires when a row is double clicked
4659          * @param {Roo.bootstrap.Table} this
4660          * @param {Roo.Element} el
4661          * @param {Number} rowIndex
4662          * @param {Roo.EventObject} e
4663          */
4664         "rowdblclick" : true,
4665         /**
4666          * @event mouseover
4667          * Fires when a mouseover occur
4668          * @param {Roo.bootstrap.Table} this
4669          * @param {Roo.Element} el
4670          * @param {Number} rowIndex
4671          * @param {Number} columnIndex
4672          * @param {Roo.EventObject} e
4673          */
4674         "mouseover" : true,
4675         /**
4676          * @event mouseout
4677          * Fires when a mouseout occur
4678          * @param {Roo.bootstrap.Table} this
4679          * @param {Roo.Element} el
4680          * @param {Number} rowIndex
4681          * @param {Number} columnIndex
4682          * @param {Roo.EventObject} e
4683          */
4684         "mouseout" : true,
4685         /**
4686          * @event rowclass
4687          * Fires when a row is rendered, so you can change add a style to it.
4688          * @param {Roo.bootstrap.Table} this
4689          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
4690          */
4691         'rowclass' : true
4692         
4693     });
4694 };
4695
4696 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
4697     
4698     cls: false,
4699     align: false,
4700     bgcolor: false,
4701     border: false,
4702     cellpadding: false,
4703     cellspacing: false,
4704     frame: false,
4705     rules: false,
4706     sortable: false,
4707     summary: false,
4708     width: false,
4709     striped : false,
4710     bordered: false,
4711     hover:  false,
4712     condensed : false,
4713     responsive : false,
4714     sm : false,
4715     cm : false,
4716     store : false,
4717     loadMask : false,
4718     tfoot : true,
4719     thead : true,
4720     RowSelection : false,
4721     CellSelection : false,
4722     layout : false,
4723     
4724     // Roo.Element - the tbody
4725     mainBody: false, 
4726     
4727     getAutoCreate : function(){
4728         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
4729         
4730         cfg = {
4731             tag: 'table',
4732             cls : 'table',
4733             cn : []
4734         }
4735             
4736         if (this.striped) {
4737             cfg.cls += ' table-striped';
4738         }
4739         
4740         if (this.hover) {
4741             cfg.cls += ' table-hover';
4742         }
4743         if (this.bordered) {
4744             cfg.cls += ' table-bordered';
4745         }
4746         if (this.condensed) {
4747             cfg.cls += ' table-condensed';
4748         }
4749         if (this.responsive) {
4750             cfg.cls += ' table-responsive';
4751         }
4752         
4753         if (this.cls) {
4754             cfg.cls+=  ' ' +this.cls;
4755         }
4756         
4757         // this lot should be simplifed...
4758         
4759         if (this.align) {
4760             cfg.align=this.align;
4761         }
4762         if (this.bgcolor) {
4763             cfg.bgcolor=this.bgcolor;
4764         }
4765         if (this.border) {
4766             cfg.border=this.border;
4767         }
4768         if (this.cellpadding) {
4769             cfg.cellpadding=this.cellpadding;
4770         }
4771         if (this.cellspacing) {
4772             cfg.cellspacing=this.cellspacing;
4773         }
4774         if (this.frame) {
4775             cfg.frame=this.frame;
4776         }
4777         if (this.rules) {
4778             cfg.rules=this.rules;
4779         }
4780         if (this.sortable) {
4781             cfg.sortable=this.sortable;
4782         }
4783         if (this.summary) {
4784             cfg.summary=this.summary;
4785         }
4786         if (this.width) {
4787             cfg.width=this.width;
4788         }
4789         if (this.layout) {
4790             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
4791         }
4792         
4793         if(this.store || this.cm){
4794             if(this.thead){
4795                 cfg.cn.push(this.renderHeader());
4796             }
4797             
4798             cfg.cn.push(this.renderBody());
4799             
4800             if(this.tfoot){
4801                 cfg.cn.push(this.renderFooter());
4802             }
4803             
4804             cfg.cls+=  ' TableGrid';
4805         }
4806         
4807         return { cn : [ cfg ] };
4808     },
4809     
4810     initEvents : function()
4811     {   
4812         if(!this.store || !this.cm){
4813             return;
4814         }
4815         
4816         //Roo.log('initEvents with ds!!!!');
4817         
4818         this.mainBody = this.el.select('tbody', true).first();
4819         
4820         
4821         var _this = this;
4822         
4823         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
4824             e.on('click', _this.sort, _this);
4825         });
4826         
4827         this.el.on("click", this.onClick, this);
4828         this.el.on("dblclick", this.onDblClick, this);
4829         
4830         this.parent().el.setStyle('position', 'relative');
4831         if (this.footer) {
4832             this.footer.parentId = this.id;
4833             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
4834         }
4835         
4836         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
4837         
4838         this.store.on('load', this.onLoad, this);
4839         this.store.on('beforeload', this.onBeforeLoad, this);
4840         this.store.on('update', this.onUpdate, this);
4841         
4842     },
4843     
4844     onMouseover : function(e, el)
4845     {
4846         var cell = Roo.get(el);
4847         
4848         if(!cell){
4849             return;
4850         }
4851         
4852         if(e.getTarget().nodeName.toLowerCase() != 'td'){
4853             cell = cell.findParent('td', false, true);
4854         }
4855         
4856         var row = cell.findParent('tr', false, true);
4857         var cellIndex = cell.dom.cellIndex;
4858         var rowIndex = row.dom.rowIndex - 1; // start from 0
4859         
4860         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
4861         
4862     },
4863     
4864     onMouseout : function(e, el)
4865     {
4866         var cell = Roo.get(el);
4867         
4868         if(!cell){
4869             return;
4870         }
4871         
4872         if(e.getTarget().nodeName.toLowerCase() != 'td'){
4873             cell = cell.findParent('td', false, true);
4874         }
4875         
4876         var row = cell.findParent('tr', false, true);
4877         var cellIndex = cell.dom.cellIndex;
4878         var rowIndex = row.dom.rowIndex - 1; // start from 0
4879         
4880         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
4881         
4882     },
4883     
4884     onClick : function(e, el)
4885     {
4886         var cell = Roo.get(el);
4887         
4888         if(!cell || (!this.CellSelection && !this.RowSelection)){
4889             return;
4890         }
4891         
4892         
4893         if(e.getTarget().nodeName.toLowerCase() != 'td'){
4894             cell = cell.findParent('td', false, true);
4895         }
4896         
4897         var row = cell.findParent('tr', false, true);
4898         var cellIndex = cell.dom.cellIndex;
4899         var rowIndex = row.dom.rowIndex - 1;
4900         
4901         if(this.CellSelection){
4902             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
4903         }
4904         
4905         if(this.RowSelection){
4906             this.fireEvent('rowclick', this, row, rowIndex, e);
4907         }
4908         
4909         
4910     },
4911     
4912     onDblClick : function(e,el)
4913     {
4914         var cell = Roo.get(el);
4915         
4916         if(!cell || (!this.CellSelection && !this.RowSelection)){
4917             return;
4918         }
4919         
4920         if(e.getTarget().nodeName.toLowerCase() != 'td'){
4921             cell = cell.findParent('td', false, true);
4922         }
4923         
4924         var row = cell.findParent('tr', false, true);
4925         var cellIndex = cell.dom.cellIndex;
4926         var rowIndex = row.dom.rowIndex - 1;
4927         
4928         if(this.CellSelection){
4929             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
4930         }
4931         
4932         if(this.RowSelection){
4933             this.fireEvent('rowdblclick', this, row, rowIndex, e);
4934         }
4935     },
4936     
4937     sort : function(e,el)
4938     {
4939         var col = Roo.get(el)
4940         
4941         if(!col.hasClass('sortable')){
4942             return;
4943         }
4944         
4945         var sort = col.attr('sort');
4946         var dir = 'ASC';
4947         
4948         if(col.hasClass('glyphicon-arrow-up')){
4949             dir = 'DESC';
4950         }
4951         
4952         this.store.sortInfo = {field : sort, direction : dir};
4953         
4954         if (this.footer) {
4955             Roo.log("calling footer first");
4956             this.footer.onClick('first');
4957         } else {
4958         
4959             this.store.load({ params : { start : 0 } });
4960         }
4961     },
4962     
4963     renderHeader : function()
4964     {
4965         var header = {
4966             tag: 'thead',
4967             cn : []
4968         };
4969         
4970         var cm = this.cm;
4971         
4972         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
4973             
4974             var config = cm.config[i];
4975                     
4976             var c = {
4977                 tag: 'th',
4978                 style : '',
4979                 html: cm.getColumnHeader(i)
4980             };
4981             
4982             if(typeof(config.hidden) != 'undefined' && config.hidden){
4983                 c.style += ' display:none;';
4984             }
4985             
4986             if(typeof(config.dataIndex) != 'undefined'){
4987                 c.sort = config.dataIndex;
4988             }
4989             
4990             if(typeof(config.sortable) != 'undefined' && config.sortable){
4991                 c.cls = 'sortable';
4992             }
4993             
4994 //            if(typeof(config.align) != 'undefined' && config.align.length){
4995 //                c.style += ' text-align:' + config.align + ';';
4996 //            }
4997             
4998             if(typeof(config.width) != 'undefined'){
4999                 c.style += ' width:' + config.width + 'px;';
5000             }
5001             
5002             header.cn.push(c)
5003         }
5004         
5005         return header;
5006     },
5007     
5008     renderBody : function()
5009     {
5010         var body = {
5011             tag: 'tbody',
5012             cn : [
5013                 {
5014                     tag: 'tr',
5015                     cn : [
5016                         {
5017                             tag : 'td',
5018                             colspan :  this.cm.getColumnCount()
5019                         }
5020                     ]
5021                 }
5022             ]
5023         };
5024         
5025         return body;
5026     },
5027     
5028     renderFooter : function()
5029     {
5030         var footer = {
5031             tag: 'tfoot',
5032             cn : [
5033                 {
5034                     tag: 'tr',
5035                     cn : [
5036                         {
5037                             tag : 'td',
5038                             colspan :  this.cm.getColumnCount()
5039                         }
5040                     ]
5041                 }
5042             ]
5043         };
5044         
5045         return footer;
5046     },
5047     
5048     
5049     
5050     onLoad : function()
5051     {
5052         Roo.log('ds onload');
5053         this.clear();
5054         
5055         var _this = this;
5056         var cm = this.cm;
5057         var ds = this.store;
5058         
5059         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5060             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
5061             
5062             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
5063                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
5064             }
5065             
5066             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
5067                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
5068             }
5069         });
5070         
5071         var tbody =  this.mainBody;
5072         
5073         var renders = [];
5074                     
5075         if(ds.getCount() > 0){
5076             ds.data.each(function(d,rowIndex){
5077                 var row =  this.renderRow(cm, ds, rowIndex);
5078                 
5079                 tbody.createChild(row);
5080                 
5081                 var _this = this;
5082                 
5083                 if(row.cellObjects.length){
5084                     Roo.each(row.cellObjects, function(r){
5085                         _this.renderCellObject(r);
5086                     })
5087                 }
5088                 
5089             }, this);
5090         }
5091         
5092         Roo.each(this.el.select('tbody td', true).elements, function(e){
5093             e.on('mouseover', _this.onMouseover, _this);
5094         });
5095         
5096         Roo.each(this.el.select('tbody td', true).elements, function(e){
5097             e.on('mouseout', _this.onMouseout, _this);
5098         });
5099
5100         //if(this.loadMask){
5101         //    this.maskEl.hide();
5102         //}
5103     },
5104     
5105     
5106     onUpdate : function(ds,record)
5107     {
5108         this.refreshRow(record);
5109     },
5110     onRemove : function(ds, record, index, isUpdate){
5111         if(isUpdate !== true){
5112             this.fireEvent("beforerowremoved", this, index, record);
5113         }
5114         var bt = this.mainBody.dom;
5115         if(bt.rows[index]){
5116             bt.removeChild(bt.rows[index]);
5117         }
5118         
5119         if(isUpdate !== true){
5120             //this.stripeRows(index);
5121             //this.syncRowHeights(index, index);
5122             //this.layout();
5123             this.fireEvent("rowremoved", this, index, record);
5124         }
5125     },
5126     
5127     
5128     refreshRow : function(record){
5129         var ds = this.store, index;
5130         if(typeof record == 'number'){
5131             index = record;
5132             record = ds.getAt(index);
5133         }else{
5134             index = ds.indexOf(record);
5135         }
5136         this.insertRow(ds, index, true);
5137         this.onRemove(ds, record, index+1, true);
5138         //this.syncRowHeights(index, index);
5139         //this.layout();
5140         this.fireEvent("rowupdated", this, index, record);
5141     },
5142     
5143     insertRow : function(dm, rowIndex, isUpdate){
5144         
5145         if(!isUpdate){
5146             this.fireEvent("beforerowsinserted", this, rowIndex);
5147         }
5148             //var s = this.getScrollState();
5149         var row = this.renderRow(this.cm, this.store, rowIndex);
5150         // insert before rowIndex..
5151         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
5152         Roo.log(e);
5153         
5154         var _this = this;
5155                 
5156         if(row.cellObjects.length){
5157             Roo.each(row.cellObjects, function(r){
5158                 _this.renderCellObject(r);
5159             })
5160         }
5161             
5162         if(!isUpdate){
5163             this.fireEvent("rowsinserted", this, rowIndex);
5164             //this.syncRowHeights(firstRow, lastRow);
5165             //this.stripeRows(firstRow);
5166             //this.layout();
5167         }
5168         
5169     },
5170     
5171     
5172     getRowDom : function(rowIndex)
5173     {
5174         // not sure if I need to check this.. but let's do it anyway..
5175         return (this.mainBody.dom.rows && (rowIndex-1) < this.mainBody.dom.rows.length ) ?
5176                 this.mainBody.dom.rows[rowIndex] : false
5177     },
5178     // returns the object tree for a tr..
5179   
5180     
5181     renderRow : function(cm, ds, rowIndex) {
5182         
5183         var d = ds.getAt(rowIndex);
5184         
5185         var row = {
5186             tag : 'tr',
5187             cn : []
5188         };
5189             
5190         var cellObjects = [];
5191         
5192         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5193             var config = cm.config[i];
5194             
5195             var renderer = cm.getRenderer(i);
5196             var value = '';
5197             var id = false;
5198             
5199             if(typeof(renderer) !== 'undefined'){
5200                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
5201             }
5202             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
5203             // and are rendered into the cells after the row is rendered - using the id for the element.
5204             
5205             if(typeof(value) === 'object'){
5206                 id = Roo.id();
5207                 cellObjects.push({
5208                     container : id,
5209                     cfg : value 
5210                 })
5211             }
5212             
5213             var rowcfg = {
5214                 record: d,
5215                 rowIndex : rowIndex,
5216                 colIndex : i,
5217                 rowClass : ''
5218             }
5219
5220             this.fireEvent('rowclass', this, rowcfg);
5221             
5222             var td = {
5223                 tag: 'td',
5224                 cls : rowcfg.rowClass,
5225                 style: '',
5226                 html: (typeof(value) === 'object') ? '' : value
5227             };
5228             
5229             if (id) {
5230                 td.id = id;
5231             }
5232             
5233             if(typeof(config.hidden) != 'undefined' && config.hidden){
5234                 td.style += ' display:none;';
5235             }
5236             
5237             if(typeof(config.align) != 'undefined' && config.align.length){
5238                 td.style += ' text-align:' + config.align + ';';
5239             }
5240             
5241             if(typeof(config.width) != 'undefined'){
5242                 td.style += ' width:' +  config.width + 'px;';
5243             }
5244              
5245             row.cn.push(td);
5246            
5247         }
5248         
5249         row.cellObjects = cellObjects;
5250         
5251         return row;
5252           
5253     },
5254     
5255     
5256     
5257     onBeforeLoad : function()
5258     {
5259         //Roo.log('ds onBeforeLoad');
5260         
5261         //this.clear();
5262         
5263         //if(this.loadMask){
5264         //    this.maskEl.show();
5265         //}
5266     },
5267     
5268     clear : function()
5269     {
5270         this.el.select('tbody', true).first().dom.innerHTML = '';
5271     },
5272     
5273     getSelectionModel : function(){
5274         if(!this.selModel){
5275             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
5276         }
5277         return this.selModel;
5278     },
5279     /*
5280      * Render the Roo.bootstrap object from renderder
5281      */
5282     renderCellObject : function(r)
5283     {
5284         var _this = this;
5285         
5286         var t = r.cfg.render(r.container);
5287         
5288         if(r.cfg.cn){
5289             Roo.each(r.cfg.cn, function(c){
5290                 var child = {
5291                     container: t.getChildContainer(),
5292                     cfg: c
5293                 }
5294                 _this.renderCellObject(child);
5295             })
5296         }
5297     }
5298    
5299 });
5300
5301  
5302
5303  /*
5304  * - LGPL
5305  *
5306  * table cell
5307  * 
5308  */
5309
5310 /**
5311  * @class Roo.bootstrap.TableCell
5312  * @extends Roo.bootstrap.Component
5313  * Bootstrap TableCell class
5314  * @cfg {String} html cell contain text
5315  * @cfg {String} cls cell class
5316  * @cfg {String} tag cell tag (td|th) default td
5317  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
5318  * @cfg {String} align Aligns the content in a cell
5319  * @cfg {String} axis Categorizes cells
5320  * @cfg {String} bgcolor Specifies the background color of a cell
5321  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5322  * @cfg {Number} colspan Specifies the number of columns a cell should span
5323  * @cfg {String} headers Specifies one or more header cells a cell is related to
5324  * @cfg {Number} height Sets the height of a cell
5325  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
5326  * @cfg {Number} rowspan Sets the number of rows a cell should span
5327  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
5328  * @cfg {String} valign Vertical aligns the content in a cell
5329  * @cfg {Number} width Specifies the width of a cell
5330  * 
5331  * @constructor
5332  * Create a new TableCell
5333  * @param {Object} config The config object
5334  */
5335
5336 Roo.bootstrap.TableCell = function(config){
5337     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
5338 };
5339
5340 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
5341     
5342     html: false,
5343     cls: false,
5344     tag: false,
5345     abbr: false,
5346     align: false,
5347     axis: false,
5348     bgcolor: false,
5349     charoff: false,
5350     colspan: false,
5351     headers: false,
5352     height: false,
5353     nowrap: false,
5354     rowspan: false,
5355     scope: false,
5356     valign: false,
5357     width: false,
5358     
5359     
5360     getAutoCreate : function(){
5361         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
5362         
5363         cfg = {
5364             tag: 'td'
5365         }
5366         
5367         if(this.tag){
5368             cfg.tag = this.tag;
5369         }
5370         
5371         if (this.html) {
5372             cfg.html=this.html
5373         }
5374         if (this.cls) {
5375             cfg.cls=this.cls
5376         }
5377         if (this.abbr) {
5378             cfg.abbr=this.abbr
5379         }
5380         if (this.align) {
5381             cfg.align=this.align
5382         }
5383         if (this.axis) {
5384             cfg.axis=this.axis
5385         }
5386         if (this.bgcolor) {
5387             cfg.bgcolor=this.bgcolor
5388         }
5389         if (this.charoff) {
5390             cfg.charoff=this.charoff
5391         }
5392         if (this.colspan) {
5393             cfg.colspan=this.colspan
5394         }
5395         if (this.headers) {
5396             cfg.headers=this.headers
5397         }
5398         if (this.height) {
5399             cfg.height=this.height
5400         }
5401         if (this.nowrap) {
5402             cfg.nowrap=this.nowrap
5403         }
5404         if (this.rowspan) {
5405             cfg.rowspan=this.rowspan
5406         }
5407         if (this.scope) {
5408             cfg.scope=this.scope
5409         }
5410         if (this.valign) {
5411             cfg.valign=this.valign
5412         }
5413         if (this.width) {
5414             cfg.width=this.width
5415         }
5416         
5417         
5418         return cfg;
5419     }
5420    
5421 });
5422
5423  
5424
5425  /*
5426  * - LGPL
5427  *
5428  * table row
5429  * 
5430  */
5431
5432 /**
5433  * @class Roo.bootstrap.TableRow
5434  * @extends Roo.bootstrap.Component
5435  * Bootstrap TableRow class
5436  * @cfg {String} cls row class
5437  * @cfg {String} align Aligns the content in a table row
5438  * @cfg {String} bgcolor Specifies a background color for a table row
5439  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5440  * @cfg {String} valign Vertical aligns the content in a table row
5441  * 
5442  * @constructor
5443  * Create a new TableRow
5444  * @param {Object} config The config object
5445  */
5446
5447 Roo.bootstrap.TableRow = function(config){
5448     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
5449 };
5450
5451 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
5452     
5453     cls: false,
5454     align: false,
5455     bgcolor: false,
5456     charoff: false,
5457     valign: false,
5458     
5459     getAutoCreate : function(){
5460         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
5461         
5462         cfg = {
5463             tag: 'tr'
5464         }
5465             
5466         if(this.cls){
5467             cfg.cls = this.cls;
5468         }
5469         if(this.align){
5470             cfg.align = this.align;
5471         }
5472         if(this.bgcolor){
5473             cfg.bgcolor = this.bgcolor;
5474         }
5475         if(this.charoff){
5476             cfg.charoff = this.charoff;
5477         }
5478         if(this.valign){
5479             cfg.valign = this.valign;
5480         }
5481         
5482         return cfg;
5483     }
5484    
5485 });
5486
5487  
5488
5489  /*
5490  * - LGPL
5491  *
5492  * table body
5493  * 
5494  */
5495
5496 /**
5497  * @class Roo.bootstrap.TableBody
5498  * @extends Roo.bootstrap.Component
5499  * Bootstrap TableBody class
5500  * @cfg {String} cls element class
5501  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
5502  * @cfg {String} align Aligns the content inside the element
5503  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
5504  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
5505  * 
5506  * @constructor
5507  * Create a new TableBody
5508  * @param {Object} config The config object
5509  */
5510
5511 Roo.bootstrap.TableBody = function(config){
5512     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
5513 };
5514
5515 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
5516     
5517     cls: false,
5518     tag: false,
5519     align: false,
5520     charoff: false,
5521     valign: false,
5522     
5523     getAutoCreate : function(){
5524         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
5525         
5526         cfg = {
5527             tag: 'tbody'
5528         }
5529             
5530         if (this.cls) {
5531             cfg.cls=this.cls
5532         }
5533         if(this.tag){
5534             cfg.tag = this.tag;
5535         }
5536         
5537         if(this.align){
5538             cfg.align = this.align;
5539         }
5540         if(this.charoff){
5541             cfg.charoff = this.charoff;
5542         }
5543         if(this.valign){
5544             cfg.valign = this.valign;
5545         }
5546         
5547         return cfg;
5548     }
5549     
5550     
5551 //    initEvents : function()
5552 //    {
5553 //        
5554 //        if(!this.store){
5555 //            return;
5556 //        }
5557 //        
5558 //        this.store = Roo.factory(this.store, Roo.data);
5559 //        this.store.on('load', this.onLoad, this);
5560 //        
5561 //        this.store.load();
5562 //        
5563 //    },
5564 //    
5565 //    onLoad: function () 
5566 //    {   
5567 //        this.fireEvent('load', this);
5568 //    }
5569 //    
5570 //   
5571 });
5572
5573  
5574
5575  /*
5576  * Based on:
5577  * Ext JS Library 1.1.1
5578  * Copyright(c) 2006-2007, Ext JS, LLC.
5579  *
5580  * Originally Released Under LGPL - original licence link has changed is not relivant.
5581  *
5582  * Fork - LGPL
5583  * <script type="text/javascript">
5584  */
5585
5586 // as we use this in bootstrap.
5587 Roo.namespace('Roo.form');
5588  /**
5589  * @class Roo.form.Action
5590  * Internal Class used to handle form actions
5591  * @constructor
5592  * @param {Roo.form.BasicForm} el The form element or its id
5593  * @param {Object} config Configuration options
5594  */
5595
5596  
5597  
5598 // define the action interface
5599 Roo.form.Action = function(form, options){
5600     this.form = form;
5601     this.options = options || {};
5602 };
5603 /**
5604  * Client Validation Failed
5605  * @const 
5606  */
5607 Roo.form.Action.CLIENT_INVALID = 'client';
5608 /**
5609  * Server Validation Failed
5610  * @const 
5611  */
5612 Roo.form.Action.SERVER_INVALID = 'server';
5613  /**
5614  * Connect to Server Failed
5615  * @const 
5616  */
5617 Roo.form.Action.CONNECT_FAILURE = 'connect';
5618 /**
5619  * Reading Data from Server Failed
5620  * @const 
5621  */
5622 Roo.form.Action.LOAD_FAILURE = 'load';
5623
5624 Roo.form.Action.prototype = {
5625     type : 'default',
5626     failureType : undefined,
5627     response : undefined,
5628     result : undefined,
5629
5630     // interface method
5631     run : function(options){
5632
5633     },
5634
5635     // interface method
5636     success : function(response){
5637
5638     },
5639
5640     // interface method
5641     handleResponse : function(response){
5642
5643     },
5644
5645     // default connection failure
5646     failure : function(response){
5647         
5648         this.response = response;
5649         this.failureType = Roo.form.Action.CONNECT_FAILURE;
5650         this.form.afterAction(this, false);
5651     },
5652
5653     processResponse : function(response){
5654         this.response = response;
5655         if(!response.responseText){
5656             return true;
5657         }
5658         this.result = this.handleResponse(response);
5659         return this.result;
5660     },
5661
5662     // utility functions used internally
5663     getUrl : function(appendParams){
5664         var url = this.options.url || this.form.url || this.form.el.dom.action;
5665         if(appendParams){
5666             var p = this.getParams();
5667             if(p){
5668                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
5669             }
5670         }
5671         return url;
5672     },
5673
5674     getMethod : function(){
5675         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
5676     },
5677
5678     getParams : function(){
5679         var bp = this.form.baseParams;
5680         var p = this.options.params;
5681         if(p){
5682             if(typeof p == "object"){
5683                 p = Roo.urlEncode(Roo.applyIf(p, bp));
5684             }else if(typeof p == 'string' && bp){
5685                 p += '&' + Roo.urlEncode(bp);
5686             }
5687         }else if(bp){
5688             p = Roo.urlEncode(bp);
5689         }
5690         return p;
5691     },
5692
5693     createCallback : function(){
5694         return {
5695             success: this.success,
5696             failure: this.failure,
5697             scope: this,
5698             timeout: (this.form.timeout*1000),
5699             upload: this.form.fileUpload ? this.success : undefined
5700         };
5701     }
5702 };
5703
5704 Roo.form.Action.Submit = function(form, options){
5705     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
5706 };
5707
5708 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
5709     type : 'submit',
5710
5711     haveProgress : false,
5712     uploadComplete : false,
5713     
5714     // uploadProgress indicator.
5715     uploadProgress : function()
5716     {
5717         if (!this.form.progressUrl) {
5718             return;
5719         }
5720         
5721         if (!this.haveProgress) {
5722             Roo.MessageBox.progress("Uploading", "Uploading");
5723         }
5724         if (this.uploadComplete) {
5725            Roo.MessageBox.hide();
5726            return;
5727         }
5728         
5729         this.haveProgress = true;
5730    
5731         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
5732         
5733         var c = new Roo.data.Connection();
5734         c.request({
5735             url : this.form.progressUrl,
5736             params: {
5737                 id : uid
5738             },
5739             method: 'GET',
5740             success : function(req){
5741                //console.log(data);
5742                 var rdata = false;
5743                 var edata;
5744                 try  {
5745                    rdata = Roo.decode(req.responseText)
5746                 } catch (e) {
5747                     Roo.log("Invalid data from server..");
5748                     Roo.log(edata);
5749                     return;
5750                 }
5751                 if (!rdata || !rdata.success) {
5752                     Roo.log(rdata);
5753                     Roo.MessageBox.alert(Roo.encode(rdata));
5754                     return;
5755                 }
5756                 var data = rdata.data;
5757                 
5758                 if (this.uploadComplete) {
5759                    Roo.MessageBox.hide();
5760                    return;
5761                 }
5762                    
5763                 if (data){
5764                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
5765                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
5766                     );
5767                 }
5768                 this.uploadProgress.defer(2000,this);
5769             },
5770        
5771             failure: function(data) {
5772                 Roo.log('progress url failed ');
5773                 Roo.log(data);
5774             },
5775             scope : this
5776         });
5777            
5778     },
5779     
5780     
5781     run : function()
5782     {
5783         // run get Values on the form, so it syncs any secondary forms.
5784         this.form.getValues();
5785         
5786         var o = this.options;
5787         var method = this.getMethod();
5788         var isPost = method == 'POST';
5789         if(o.clientValidation === false || this.form.isValid()){
5790             
5791             if (this.form.progressUrl) {
5792                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
5793                     (new Date() * 1) + '' + Math.random());
5794                     
5795             } 
5796             
5797             
5798             Roo.Ajax.request(Roo.apply(this.createCallback(), {
5799                 form:this.form.el.dom,
5800                 url:this.getUrl(!isPost),
5801                 method: method,
5802                 params:isPost ? this.getParams() : null,
5803                 isUpload: this.form.fileUpload
5804             }));
5805             
5806             this.uploadProgress();
5807
5808         }else if (o.clientValidation !== false){ // client validation failed
5809             this.failureType = Roo.form.Action.CLIENT_INVALID;
5810             this.form.afterAction(this, false);
5811         }
5812     },
5813
5814     success : function(response)
5815     {
5816         this.uploadComplete= true;
5817         if (this.haveProgress) {
5818             Roo.MessageBox.hide();
5819         }
5820         
5821         
5822         var result = this.processResponse(response);
5823         if(result === true || result.success){
5824             this.form.afterAction(this, true);
5825             return;
5826         }
5827         if(result.errors){
5828             this.form.markInvalid(result.errors);
5829             this.failureType = Roo.form.Action.SERVER_INVALID;
5830         }
5831         this.form.afterAction(this, false);
5832     },
5833     failure : function(response)
5834     {
5835         this.uploadComplete= true;
5836         if (this.haveProgress) {
5837             Roo.MessageBox.hide();
5838         }
5839         
5840         this.response = response;
5841         this.failureType = Roo.form.Action.CONNECT_FAILURE;
5842         this.form.afterAction(this, false);
5843     },
5844     
5845     handleResponse : function(response){
5846         if(this.form.errorReader){
5847             var rs = this.form.errorReader.read(response);
5848             var errors = [];
5849             if(rs.records){
5850                 for(var i = 0, len = rs.records.length; i < len; i++) {
5851                     var r = rs.records[i];
5852                     errors[i] = r.data;
5853                 }
5854             }
5855             if(errors.length < 1){
5856                 errors = null;
5857             }
5858             return {
5859                 success : rs.success,
5860                 errors : errors
5861             };
5862         }
5863         var ret = false;
5864         try {
5865             ret = Roo.decode(response.responseText);
5866         } catch (e) {
5867             ret = {
5868                 success: false,
5869                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
5870                 errors : []
5871             };
5872         }
5873         return ret;
5874         
5875     }
5876 });
5877
5878
5879 Roo.form.Action.Load = function(form, options){
5880     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
5881     this.reader = this.form.reader;
5882 };
5883
5884 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
5885     type : 'load',
5886
5887     run : function(){
5888         
5889         Roo.Ajax.request(Roo.apply(
5890                 this.createCallback(), {
5891                     method:this.getMethod(),
5892                     url:this.getUrl(false),
5893                     params:this.getParams()
5894         }));
5895     },
5896
5897     success : function(response){
5898         
5899         var result = this.processResponse(response);
5900         if(result === true || !result.success || !result.data){
5901             this.failureType = Roo.form.Action.LOAD_FAILURE;
5902             this.form.afterAction(this, false);
5903             return;
5904         }
5905         this.form.clearInvalid();
5906         this.form.setValues(result.data);
5907         this.form.afterAction(this, true);
5908     },
5909
5910     handleResponse : function(response){
5911         if(this.form.reader){
5912             var rs = this.form.reader.read(response);
5913             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
5914             return {
5915                 success : rs.success,
5916                 data : data
5917             };
5918         }
5919         return Roo.decode(response.responseText);
5920     }
5921 });
5922
5923 Roo.form.Action.ACTION_TYPES = {
5924     'load' : Roo.form.Action.Load,
5925     'submit' : Roo.form.Action.Submit
5926 };/*
5927  * - LGPL
5928  *
5929  * form
5930  * 
5931  */
5932
5933 /**
5934  * @class Roo.bootstrap.Form
5935  * @extends Roo.bootstrap.Component
5936  * Bootstrap Form class
5937  * @cfg {String} method  GET | POST (default POST)
5938  * @cfg {String} labelAlign top | left (default top)
5939   * @cfg {String} align left  | right - for navbars
5940
5941  * 
5942  * @constructor
5943  * Create a new Form
5944  * @param {Object} config The config object
5945  */
5946
5947
5948 Roo.bootstrap.Form = function(config){
5949     Roo.bootstrap.Form.superclass.constructor.call(this, config);
5950     this.addEvents({
5951         /**
5952          * @event clientvalidation
5953          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
5954          * @param {Form} this
5955          * @param {Boolean} valid true if the form has passed client-side validation
5956          */
5957         clientvalidation: true,
5958         /**
5959          * @event beforeaction
5960          * Fires before any action is performed. Return false to cancel the action.
5961          * @param {Form} this
5962          * @param {Action} action The action to be performed
5963          */
5964         beforeaction: true,
5965         /**
5966          * @event actionfailed
5967          * Fires when an action fails.
5968          * @param {Form} this
5969          * @param {Action} action The action that failed
5970          */
5971         actionfailed : true,
5972         /**
5973          * @event actioncomplete
5974          * Fires when an action is completed.
5975          * @param {Form} this
5976          * @param {Action} action The action that completed
5977          */
5978         actioncomplete : true
5979     });
5980     
5981 };
5982
5983 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
5984       
5985      /**
5986      * @cfg {String} method
5987      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
5988      */
5989     method : 'POST',
5990     /**
5991      * @cfg {String} url
5992      * The URL to use for form actions if one isn't supplied in the action options.
5993      */
5994     /**
5995      * @cfg {Boolean} fileUpload
5996      * Set to true if this form is a file upload.
5997      */
5998      
5999     /**
6000      * @cfg {Object} baseParams
6001      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
6002      */
6003       
6004     /**
6005      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
6006      */
6007     timeout: 30,
6008     /**
6009      * @cfg {Sting} align (left|right) for navbar forms
6010      */
6011     align : 'left',
6012
6013     // private
6014     activeAction : null,
6015  
6016     /**
6017      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6018      * element by passing it or its id or mask the form itself by passing in true.
6019      * @type Mixed
6020      */
6021     waitMsgTarget : false,
6022     
6023      
6024     
6025     /**
6026      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6027      * element by passing it or its id or mask the form itself by passing in true.
6028      * @type Mixed
6029      */
6030     
6031     getAutoCreate : function(){
6032         
6033         var cfg = {
6034             tag: 'form',
6035             method : this.method || 'POST',
6036             id : this.id || Roo.id(),
6037             cls : ''
6038         }
6039         if (this.parent().xtype.match(/^Nav/)) {
6040             cfg.cls = 'navbar-form navbar-' + this.align;
6041             
6042         }
6043         
6044         if (this.labelAlign == 'left' ) {
6045             cfg.cls += ' form-horizontal';
6046         }
6047         
6048         
6049         return cfg;
6050     },
6051     initEvents : function()
6052     {
6053         this.el.on('submit', this.onSubmit, this);
6054         // this was added as random key presses on the form where triggering form submit.
6055         this.el.on('keypress', function(e) {
6056             if (e.getCharCode() != 13) {
6057                 return true;
6058             }
6059             // we might need to allow it for textareas.. and some other items.
6060             // check e.getTarget().
6061             
6062             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
6063                 return true;
6064             }
6065         
6066             Roo.log("keypress blocked");
6067             
6068             e.preventDefault();
6069             return false;
6070         });
6071         
6072     },
6073     // private
6074     onSubmit : function(e){
6075         e.stopEvent();
6076     },
6077     
6078      /**
6079      * Returns true if client-side validation on the form is successful.
6080      * @return Boolean
6081      */
6082     isValid : function(){
6083         var items = this.getItems();
6084         var valid = true;
6085         items.each(function(f){
6086            if(!f.validate()){
6087                valid = false;
6088                
6089            }
6090         });
6091         return valid;
6092     },
6093     /**
6094      * Returns true if any fields in this form have changed since their original load.
6095      * @return Boolean
6096      */
6097     isDirty : function(){
6098         var dirty = false;
6099         var items = this.getItems();
6100         items.each(function(f){
6101            if(f.isDirty()){
6102                dirty = true;
6103                return false;
6104            }
6105            return true;
6106         });
6107         return dirty;
6108     },
6109      /**
6110      * Performs a predefined action (submit or load) or custom actions you define on this form.
6111      * @param {String} actionName The name of the action type
6112      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
6113      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
6114      * accept other config options):
6115      * <pre>
6116 Property          Type             Description
6117 ----------------  ---------------  ----------------------------------------------------------------------------------
6118 url               String           The url for the action (defaults to the form's url)
6119 method            String           The form method to use (defaults to the form's method, or POST if not defined)
6120 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
6121 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
6122                                    validate the form on the client (defaults to false)
6123      * </pre>
6124      * @return {BasicForm} this
6125      */
6126     doAction : function(action, options){
6127         if(typeof action == 'string'){
6128             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
6129         }
6130         if(this.fireEvent('beforeaction', this, action) !== false){
6131             this.beforeAction(action);
6132             action.run.defer(100, action);
6133         }
6134         return this;
6135     },
6136     
6137     // private
6138     beforeAction : function(action){
6139         var o = action.options;
6140         
6141         // not really supported yet.. ??
6142         
6143         //if(this.waitMsgTarget === true){
6144             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6145         //}else if(this.waitMsgTarget){
6146         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
6147         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
6148         //}else {
6149         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
6150        // }
6151          
6152     },
6153
6154     // private
6155     afterAction : function(action, success){
6156         this.activeAction = null;
6157         var o = action.options;
6158         
6159         //if(this.waitMsgTarget === true){
6160             this.el.unmask();
6161         //}else if(this.waitMsgTarget){
6162         //    this.waitMsgTarget.unmask();
6163         //}else{
6164         //    Roo.MessageBox.updateProgress(1);
6165         //    Roo.MessageBox.hide();
6166        // }
6167         // 
6168         if(success){
6169             if(o.reset){
6170                 this.reset();
6171             }
6172             Roo.callback(o.success, o.scope, [this, action]);
6173             this.fireEvent('actioncomplete', this, action);
6174             
6175         }else{
6176             
6177             // failure condition..
6178             // we have a scenario where updates need confirming.
6179             // eg. if a locking scenario exists..
6180             // we look for { errors : { needs_confirm : true }} in the response.
6181             if (
6182                 (typeof(action.result) != 'undefined')  &&
6183                 (typeof(action.result.errors) != 'undefined')  &&
6184                 (typeof(action.result.errors.needs_confirm) != 'undefined')
6185            ){
6186                 var _t = this;
6187                 Roo.log("not supported yet");
6188                  /*
6189                 
6190                 Roo.MessageBox.confirm(
6191                     "Change requires confirmation",
6192                     action.result.errorMsg,
6193                     function(r) {
6194                         if (r != 'yes') {
6195                             return;
6196                         }
6197                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
6198                     }
6199                     
6200                 );
6201                 */
6202                 
6203                 
6204                 return;
6205             }
6206             
6207             Roo.callback(o.failure, o.scope, [this, action]);
6208             // show an error message if no failed handler is set..
6209             if (!this.hasListener('actionfailed')) {
6210                 Roo.log("need to add dialog support");
6211                 /*
6212                 Roo.MessageBox.alert("Error",
6213                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
6214                         action.result.errorMsg :
6215                         "Saving Failed, please check your entries or try again"
6216                 );
6217                 */
6218             }
6219             
6220             this.fireEvent('actionfailed', this, action);
6221         }
6222         
6223     },
6224     /**
6225      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
6226      * @param {String} id The value to search for
6227      * @return Field
6228      */
6229     findField : function(id){
6230         var items = this.getItems();
6231         var field = items.get(id);
6232         if(!field){
6233              items.each(function(f){
6234                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
6235                     field = f;
6236                     return false;
6237                 }
6238                 return true;
6239             });
6240         }
6241         return field || null;
6242     },
6243      /**
6244      * Mark fields in this form invalid in bulk.
6245      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
6246      * @return {BasicForm} this
6247      */
6248     markInvalid : function(errors){
6249         if(errors instanceof Array){
6250             for(var i = 0, len = errors.length; i < len; i++){
6251                 var fieldError = errors[i];
6252                 var f = this.findField(fieldError.id);
6253                 if(f){
6254                     f.markInvalid(fieldError.msg);
6255                 }
6256             }
6257         }else{
6258             var field, id;
6259             for(id in errors){
6260                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
6261                     field.markInvalid(errors[id]);
6262                 }
6263             }
6264         }
6265         //Roo.each(this.childForms || [], function (f) {
6266         //    f.markInvalid(errors);
6267         //});
6268         
6269         return this;
6270     },
6271
6272     /**
6273      * Set values for fields in this form in bulk.
6274      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
6275      * @return {BasicForm} this
6276      */
6277     setValues : function(values){
6278         if(values instanceof Array){ // array of objects
6279             for(var i = 0, len = values.length; i < len; i++){
6280                 var v = values[i];
6281                 var f = this.findField(v.id);
6282                 if(f){
6283                     f.setValue(v.value);
6284                     if(this.trackResetOnLoad){
6285                         f.originalValue = f.getValue();
6286                     }
6287                 }
6288             }
6289         }else{ // object hash
6290             var field, id;
6291             for(id in values){
6292                 if(typeof values[id] != 'function' && (field = this.findField(id))){
6293                     
6294                     if (field.setFromData && 
6295                         field.valueField && 
6296                         field.displayField &&
6297                         // combos' with local stores can 
6298                         // be queried via setValue()
6299                         // to set their value..
6300                         (field.store && !field.store.isLocal)
6301                         ) {
6302                         // it's a combo
6303                         var sd = { };
6304                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
6305                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
6306                         field.setFromData(sd);
6307                         
6308                     } else {
6309                         field.setValue(values[id]);
6310                     }
6311                     
6312                     
6313                     if(this.trackResetOnLoad){
6314                         field.originalValue = field.getValue();
6315                     }
6316                 }
6317             }
6318         }
6319          
6320         //Roo.each(this.childForms || [], function (f) {
6321         //    f.setValues(values);
6322         //});
6323                 
6324         return this;
6325     },
6326
6327     /**
6328      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
6329      * they are returned as an array.
6330      * @param {Boolean} asString
6331      * @return {Object}
6332      */
6333     getValues : function(asString){
6334         //if (this.childForms) {
6335             // copy values from the child forms
6336         //    Roo.each(this.childForms, function (f) {
6337         //        this.setValues(f.getValues());
6338         //    }, this);
6339         //}
6340         
6341         
6342         
6343         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
6344         if(asString === true){
6345             return fs;
6346         }
6347         return Roo.urlDecode(fs);
6348     },
6349     
6350     /**
6351      * Returns the fields in this form as an object with key/value pairs. 
6352      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
6353      * @return {Object}
6354      */
6355     getFieldValues : function(with_hidden)
6356     {
6357         var items = this.getItems();
6358         var ret = {};
6359         items.each(function(f){
6360             if (!f.getName()) {
6361                 return;
6362             }
6363             var v = f.getValue();
6364             if (f.inputType =='radio') {
6365                 if (typeof(ret[f.getName()]) == 'undefined') {
6366                     ret[f.getName()] = ''; // empty..
6367                 }
6368                 
6369                 if (!f.el.dom.checked) {
6370                     return;
6371                     
6372                 }
6373                 v = f.el.dom.value;
6374                 
6375             }
6376             
6377             // not sure if this supported any more..
6378             if ((typeof(v) == 'object') && f.getRawValue) {
6379                 v = f.getRawValue() ; // dates..
6380             }
6381             // combo boxes where name != hiddenName...
6382             if (f.name != f.getName()) {
6383                 ret[f.name] = f.getRawValue();
6384             }
6385             ret[f.getName()] = v;
6386         });
6387         
6388         return ret;
6389     },
6390
6391     /**
6392      * Clears all invalid messages in this form.
6393      * @return {BasicForm} this
6394      */
6395     clearInvalid : function(){
6396         var items = this.getItems();
6397         
6398         items.each(function(f){
6399            f.clearInvalid();
6400         });
6401         
6402         
6403         
6404         return this;
6405     },
6406
6407     /**
6408      * Resets this form.
6409      * @return {BasicForm} this
6410      */
6411     reset : function(){
6412         var items = this.getItems();
6413         items.each(function(f){
6414             f.reset();
6415         });
6416         
6417         Roo.each(this.childForms || [], function (f) {
6418             f.reset();
6419         });
6420        
6421         
6422         return this;
6423     },
6424     getItems : function()
6425     {
6426         var r=new Roo.util.MixedCollection(false, function(o){
6427             return o.id || (o.id = Roo.id());
6428         });
6429         var iter = function(el) {
6430             if (el.inputEl) {
6431                 r.add(el);
6432             }
6433             if (!el.items) {
6434                 return;
6435             }
6436             Roo.each(el.items,function(e) {
6437                 iter(e);
6438             });
6439             
6440             
6441         };
6442         iter(this);
6443         return r;
6444         
6445         
6446         
6447         
6448     }
6449     
6450 });
6451
6452  
6453 /*
6454  * Based on:
6455  * Ext JS Library 1.1.1
6456  * Copyright(c) 2006-2007, Ext JS, LLC.
6457  *
6458  * Originally Released Under LGPL - original licence link has changed is not relivant.
6459  *
6460  * Fork - LGPL
6461  * <script type="text/javascript">
6462  */
6463 /**
6464  * @class Roo.form.VTypes
6465  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
6466  * @singleton
6467  */
6468 Roo.form.VTypes = function(){
6469     // closure these in so they are only created once.
6470     var alpha = /^[a-zA-Z_]+$/;
6471     var alphanum = /^[a-zA-Z0-9_]+$/;
6472     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
6473     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
6474
6475     // All these messages and functions are configurable
6476     return {
6477         /**
6478          * The function used to validate email addresses
6479          * @param {String} value The email address
6480          */
6481         'email' : function(v){
6482             return email.test(v);
6483         },
6484         /**
6485          * The error text to display when the email validation function returns false
6486          * @type String
6487          */
6488         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
6489         /**
6490          * The keystroke filter mask to be applied on email input
6491          * @type RegExp
6492          */
6493         'emailMask' : /[a-z0-9_\.\-@]/i,
6494
6495         /**
6496          * The function used to validate URLs
6497          * @param {String} value The URL
6498          */
6499         'url' : function(v){
6500             return url.test(v);
6501         },
6502         /**
6503          * The error text to display when the url validation function returns false
6504          * @type String
6505          */
6506         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
6507         
6508         /**
6509          * The function used to validate alpha values
6510          * @param {String} value The value
6511          */
6512         'alpha' : function(v){
6513             return alpha.test(v);
6514         },
6515         /**
6516          * The error text to display when the alpha validation function returns false
6517          * @type String
6518          */
6519         'alphaText' : 'This field should only contain letters and _',
6520         /**
6521          * The keystroke filter mask to be applied on alpha input
6522          * @type RegExp
6523          */
6524         'alphaMask' : /[a-z_]/i,
6525
6526         /**
6527          * The function used to validate alphanumeric values
6528          * @param {String} value The value
6529          */
6530         'alphanum' : function(v){
6531             return alphanum.test(v);
6532         },
6533         /**
6534          * The error text to display when the alphanumeric validation function returns false
6535          * @type String
6536          */
6537         'alphanumText' : 'This field should only contain letters, numbers and _',
6538         /**
6539          * The keystroke filter mask to be applied on alphanumeric input
6540          * @type RegExp
6541          */
6542         'alphanumMask' : /[a-z0-9_]/i
6543     };
6544 }();/*
6545  * - LGPL
6546  *
6547  * Input
6548  * 
6549  */
6550
6551 /**
6552  * @class Roo.bootstrap.Input
6553  * @extends Roo.bootstrap.Component
6554  * Bootstrap Input class
6555  * @cfg {Boolean} disabled is it disabled
6556  * @cfg {String} fieldLabel - the label associated
6557  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
6558  * @cfg {String} name name of the input
6559  * @cfg {string} fieldLabel - the label associated
6560  * @cfg {string}  inputType - input / file submit ...
6561  * @cfg {string} placeholder - placeholder to put in text.
6562  * @cfg {string}  before - input group add on before
6563  * @cfg {string} after - input group add on after
6564  * @cfg {string} size - (lg|sm) or leave empty..
6565  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
6566  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
6567  * @cfg {Number} md colspan out of 12 for computer-sized screens
6568  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
6569  * @cfg {string} value default value of the input
6570  * @cfg {Number} labelWidth set the width of label (0-12)
6571  * @cfg {String} labelAlign (top|left)
6572  * @cfg {Boolean} readOnly Specifies that the field should be read-only
6573  * @cfg {String} align (left|center|right) Default left
6574  * 
6575  * 
6576  * @constructor
6577  * Create a new Input
6578  * @param {Object} config The config object
6579  */
6580
6581 Roo.bootstrap.Input = function(config){
6582     Roo.bootstrap.Input.superclass.constructor.call(this, config);
6583    
6584         this.addEvents({
6585             /**
6586              * @event focus
6587              * Fires when this field receives input focus.
6588              * @param {Roo.form.Field} this
6589              */
6590             focus : true,
6591             /**
6592              * @event blur
6593              * Fires when this field loses input focus.
6594              * @param {Roo.form.Field} this
6595              */
6596             blur : true,
6597             /**
6598              * @event specialkey
6599              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
6600              * {@link Roo.EventObject#getKey} to determine which key was pressed.
6601              * @param {Roo.form.Field} this
6602              * @param {Roo.EventObject} e The event object
6603              */
6604             specialkey : true,
6605             /**
6606              * @event change
6607              * Fires just before the field blurs if the field value has changed.
6608              * @param {Roo.form.Field} this
6609              * @param {Mixed} newValue The new value
6610              * @param {Mixed} oldValue The original value
6611              */
6612             change : true,
6613             /**
6614              * @event invalid
6615              * Fires after the field has been marked as invalid.
6616              * @param {Roo.form.Field} this
6617              * @param {String} msg The validation message
6618              */
6619             invalid : true,
6620             /**
6621              * @event valid
6622              * Fires after the field has been validated with no errors.
6623              * @param {Roo.form.Field} this
6624              */
6625             valid : true,
6626              /**
6627              * @event keyup
6628              * Fires after the key up
6629              * @param {Roo.form.Field} this
6630              * @param {Roo.EventObject}  e The event Object
6631              */
6632             keyup : true
6633         });
6634 };
6635
6636 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
6637      /**
6638      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
6639       automatic validation (defaults to "keyup").
6640      */
6641     validationEvent : "keyup",
6642      /**
6643      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
6644      */
6645     validateOnBlur : true,
6646     /**
6647      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
6648      */
6649     validationDelay : 250,
6650      /**
6651      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
6652      */
6653     focusClass : "x-form-focus",  // not needed???
6654     
6655        
6656     /**
6657      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
6658      */
6659     invalidClass : "has-error",
6660     
6661     /**
6662      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
6663      */
6664     selectOnFocus : false,
6665     
6666      /**
6667      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
6668      */
6669     maskRe : null,
6670        /**
6671      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
6672      */
6673     vtype : null,
6674     
6675       /**
6676      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
6677      */
6678     disableKeyFilter : false,
6679     
6680        /**
6681      * @cfg {Boolean} disabled True to disable the field (defaults to false).
6682      */
6683     disabled : false,
6684      /**
6685      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
6686      */
6687     allowBlank : true,
6688     /**
6689      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
6690      */
6691     blankText : "This field is required",
6692     
6693      /**
6694      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
6695      */
6696     minLength : 0,
6697     /**
6698      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
6699      */
6700     maxLength : Number.MAX_VALUE,
6701     /**
6702      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
6703      */
6704     minLengthText : "The minimum length for this field is {0}",
6705     /**
6706      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
6707      */
6708     maxLengthText : "The maximum length for this field is {0}",
6709   
6710     
6711     /**
6712      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
6713      * If available, this function will be called only after the basic validators all return true, and will be passed the
6714      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
6715      */
6716     validator : null,
6717     /**
6718      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
6719      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
6720      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
6721      */
6722     regex : null,
6723     /**
6724      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
6725      */
6726     regexText : "",
6727     
6728     
6729     
6730     fieldLabel : '',
6731     inputType : 'text',
6732     
6733     name : false,
6734     placeholder: false,
6735     before : false,
6736     after : false,
6737     size : false,
6738     // private
6739     hasFocus : false,
6740     preventMark: false,
6741     isFormField : true,
6742     value : '',
6743     labelWidth : 2,
6744     labelAlign : false,
6745     readOnly : false,
6746     align : false,
6747     formatedValue : false,
6748     
6749     parentLabelAlign : function()
6750     {
6751         var parent = this;
6752         while (parent.parent()) {
6753             parent = parent.parent();
6754             if (typeof(parent.labelAlign) !='undefined') {
6755                 return parent.labelAlign;
6756             }
6757         }
6758         return 'left';
6759         
6760     },
6761     
6762     getAutoCreate : function(){
6763         
6764         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
6765         
6766         var id = Roo.id();
6767         
6768         var cfg = {};
6769         
6770         if(this.inputType != 'hidden'){
6771             cfg.cls = 'form-group' //input-group
6772         }
6773         
6774         var input =  {
6775             tag: 'input',
6776             id : id,
6777             type : this.inputType,
6778             value : this.value,
6779             cls : 'form-control',
6780             placeholder : this.placeholder || ''
6781             
6782         };
6783         
6784         if(this.align){
6785             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
6786         }
6787         
6788         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
6789             input.maxLength = this.maxLength;
6790         }
6791         
6792         if (this.disabled) {
6793             input.disabled=true;
6794         }
6795         
6796         if (this.readOnly) {
6797             input.readonly=true;
6798         }
6799         
6800         if (this.name) {
6801             input.name = this.name;
6802         }
6803         if (this.size) {
6804             input.cls += ' input-' + this.size;
6805         }
6806         var settings=this;
6807         ['xs','sm','md','lg'].map(function(size){
6808             if (settings[size]) {
6809                 cfg.cls += ' col-' + size + '-' + settings[size];
6810             }
6811         });
6812         
6813         var inputblock = input;
6814         
6815         if (this.before || this.after) {
6816             
6817             inputblock = {
6818                 cls : 'input-group',
6819                 cn :  [] 
6820             };
6821             if (this.before && typeof(this.before) == 'string') {
6822                 
6823                 inputblock.cn.push({
6824                     tag :'span',
6825                     cls : 'roo-input-before input-group-addon',
6826                     html : this.before
6827                 });
6828             }
6829             if (this.before && typeof(this.before) == 'object') {
6830                 this.before = Roo.factory(this.before);
6831                 Roo.log(this.before);
6832                 inputblock.cn.push({
6833                     tag :'span',
6834                     cls : 'roo-input-before input-group-' +
6835                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
6836                 });
6837             }
6838             
6839             inputblock.cn.push(input);
6840             
6841             if (this.after && typeof(this.after) == 'string') {
6842                 inputblock.cn.push({
6843                     tag :'span',
6844                     cls : 'roo-input-after input-group-addon',
6845                     html : this.after
6846                 });
6847             }
6848             if (this.after && typeof(this.after) == 'object') {
6849                 this.after = Roo.factory(this.after);
6850                 Roo.log(this.after);
6851                 inputblock.cn.push({
6852                     tag :'span',
6853                     cls : 'roo-input-after input-group-' +
6854                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
6855                 });
6856             }
6857         };
6858         
6859         if (align ==='left' && this.fieldLabel.length) {
6860                 Roo.log("left and has label");
6861                 cfg.cn = [
6862                     
6863                     {
6864                         tag: 'label',
6865                         'for' :  id,
6866                         cls : 'control-label col-sm-' + this.labelWidth,
6867                         html : this.fieldLabel
6868                         
6869                     },
6870                     {
6871                         cls : "col-sm-" + (12 - this.labelWidth), 
6872                         cn: [
6873                             inputblock
6874                         ]
6875                     }
6876                     
6877                 ];
6878         } else if ( this.fieldLabel.length) {
6879                 Roo.log(" label");
6880                  cfg.cn = [
6881                    
6882                     {
6883                         tag: 'label',
6884                         //cls : 'input-group-addon',
6885                         html : this.fieldLabel
6886                         
6887                     },
6888                     
6889                     inputblock
6890                     
6891                 ];
6892
6893         } else {
6894             
6895                 Roo.log(" no label && no align");
6896                 cfg.cn = [
6897                     
6898                         inputblock
6899                     
6900                 ];
6901                 
6902                 
6903         };
6904         Roo.log('input-parentType: ' + this.parentType);
6905         
6906         if (this.parentType === 'Navbar' &&  this.parent().bar) {
6907            cfg.cls += ' navbar-form';
6908            Roo.log(cfg);
6909         }
6910         
6911         return cfg;
6912         
6913     },
6914     /**
6915      * return the real input element.
6916      */
6917     inputEl: function ()
6918     {
6919         return this.el.select('input.form-control',true).first();
6920     },
6921     setDisabled : function(v)
6922     {
6923         var i  = this.inputEl().dom;
6924         if (!v) {
6925             i.removeAttribute('disabled');
6926             return;
6927             
6928         }
6929         i.setAttribute('disabled','true');
6930     },
6931     initEvents : function()
6932     {
6933         
6934         this.inputEl().on("keydown" , this.fireKey,  this);
6935         this.inputEl().on("focus", this.onFocus,  this);
6936         this.inputEl().on("blur", this.onBlur,  this);
6937         
6938         this.inputEl().relayEvent('keyup', this);
6939
6940         // reference to original value for reset
6941         this.originalValue = this.getValue();
6942         //Roo.form.TextField.superclass.initEvents.call(this);
6943         if(this.validationEvent == 'keyup'){
6944             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
6945             this.inputEl().on('keyup', this.filterValidation, this);
6946         }
6947         else if(this.validationEvent !== false){
6948             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
6949         }
6950         
6951         if(this.selectOnFocus){
6952             this.on("focus", this.preFocus, this);
6953             
6954         }
6955         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
6956             this.inputEl().on("keypress", this.filterKeys, this);
6957         }
6958        /* if(this.grow){
6959             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
6960             this.el.on("click", this.autoSize,  this);
6961         }
6962         */
6963         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
6964             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
6965         }
6966         
6967         if (typeof(this.before) == 'object') {
6968             this.before.render(this.el.select('.roo-input-before',true).first());
6969         }
6970         if (typeof(this.after) == 'object') {
6971             this.after.render(this.el.select('.roo-input-after',true).first());
6972         }
6973         
6974         
6975     },
6976     filterValidation : function(e){
6977         if(!e.isNavKeyPress()){
6978             this.validationTask.delay(this.validationDelay);
6979         }
6980     },
6981      /**
6982      * Validates the field value
6983      * @return {Boolean} True if the value is valid, else false
6984      */
6985     validate : function(){
6986         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
6987         if(this.disabled || this.validateValue(this.getRawValue())){
6988             this.clearInvalid();
6989             return true;
6990         }
6991         return false;
6992     },
6993     
6994     
6995     /**
6996      * Validates a value according to the field's validation rules and marks the field as invalid
6997      * if the validation fails
6998      * @param {Mixed} value The value to validate
6999      * @return {Boolean} True if the value is valid, else false
7000      */
7001     validateValue : function(value){
7002         if(value.length < 1)  { // if it's blank
7003              if(this.allowBlank){
7004                 this.clearInvalid();
7005                 return true;
7006              }else{
7007                 this.markInvalid(this.blankText);
7008                 return false;
7009              }
7010         }
7011         if(value.length < this.minLength){
7012             this.markInvalid(String.format(this.minLengthText, this.minLength));
7013             return false;
7014         }
7015         if(value.length > this.maxLength){
7016             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
7017             return false;
7018         }
7019         if(this.vtype){
7020             var vt = Roo.form.VTypes;
7021             if(!vt[this.vtype](value, this)){
7022                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
7023                 return false;
7024             }
7025         }
7026         if(typeof this.validator == "function"){
7027             var msg = this.validator(value);
7028             if(msg !== true){
7029                 this.markInvalid(msg);
7030                 return false;
7031             }
7032         }
7033         if(this.regex && !this.regex.test(value)){
7034             this.markInvalid(this.regexText);
7035             return false;
7036         }
7037         return true;
7038     },
7039
7040     
7041     
7042      // private
7043     fireKey : function(e){
7044         //Roo.log('field ' + e.getKey());
7045         if(e.isNavKeyPress()){
7046             this.fireEvent("specialkey", this, e);
7047         }
7048     },
7049     focus : function (selectText){
7050         if(this.rendered){
7051             this.inputEl().focus();
7052             if(selectText === true){
7053                 this.inputEl().dom.select();
7054             }
7055         }
7056         return this;
7057     } ,
7058     
7059     onFocus : function(){
7060         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7061            // this.el.addClass(this.focusClass);
7062         }
7063         if(!this.hasFocus){
7064             this.hasFocus = true;
7065             this.startValue = this.getValue();
7066             this.fireEvent("focus", this);
7067         }
7068     },
7069     
7070     beforeBlur : Roo.emptyFn,
7071
7072     
7073     // private
7074     onBlur : function(){
7075         this.beforeBlur();
7076         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7077             //this.el.removeClass(this.focusClass);
7078         }
7079         this.hasFocus = false;
7080         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
7081             this.validate();
7082         }
7083         var v = this.getValue();
7084         if(String(v) !== String(this.startValue)){
7085             this.fireEvent('change', this, v, this.startValue);
7086         }
7087         this.fireEvent("blur", this);
7088     },
7089     
7090     /**
7091      * Resets the current field value to the originally loaded value and clears any validation messages
7092      */
7093     reset : function(){
7094         this.setValue(this.originalValue);
7095         this.clearInvalid();
7096     },
7097      /**
7098      * Returns the name of the field
7099      * @return {Mixed} name The name field
7100      */
7101     getName: function(){
7102         return this.name;
7103     },
7104      /**
7105      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
7106      * @return {Mixed} value The field value
7107      */
7108     getValue : function(){
7109         
7110         var v = this.inputEl().getValue();
7111         
7112         return v;
7113     },
7114     /**
7115      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
7116      * @return {Mixed} value The field value
7117      */
7118     getRawValue : function(){
7119         var v = this.inputEl().getValue();
7120         
7121         return v;
7122     },
7123     
7124     /**
7125      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
7126      * @param {Mixed} value The value to set
7127      */
7128     setRawValue : function(v){
7129         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7130     },
7131     
7132     selectText : function(start, end){
7133         var v = this.getRawValue();
7134         if(v.length > 0){
7135             start = start === undefined ? 0 : start;
7136             end = end === undefined ? v.length : end;
7137             var d = this.inputEl().dom;
7138             if(d.setSelectionRange){
7139                 d.setSelectionRange(start, end);
7140             }else if(d.createTextRange){
7141                 var range = d.createTextRange();
7142                 range.moveStart("character", start);
7143                 range.moveEnd("character", v.length-end);
7144                 range.select();
7145             }
7146         }
7147     },
7148     
7149     /**
7150      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
7151      * @param {Mixed} value The value to set
7152      */
7153     setValue : function(v){
7154         this.value = v;
7155         if(this.rendered){
7156             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7157             this.validate();
7158         }
7159     },
7160     
7161     /*
7162     processValue : function(value){
7163         if(this.stripCharsRe){
7164             var newValue = value.replace(this.stripCharsRe, '');
7165             if(newValue !== value){
7166                 this.setRawValue(newValue);
7167                 return newValue;
7168             }
7169         }
7170         return value;
7171     },
7172   */
7173     preFocus : function(){
7174         
7175         if(this.selectOnFocus){
7176             this.inputEl().dom.select();
7177         }
7178     },
7179     filterKeys : function(e){
7180         var k = e.getKey();
7181         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
7182             return;
7183         }
7184         var c = e.getCharCode(), cc = String.fromCharCode(c);
7185         if(Roo.isIE && (e.isSpecialKey() || !cc)){
7186             return;
7187         }
7188         if(!this.maskRe.test(cc)){
7189             e.stopEvent();
7190         }
7191     },
7192      /**
7193      * Clear any invalid styles/messages for this field
7194      */
7195     clearInvalid : function(){
7196         
7197         if(!this.el || this.preventMark){ // not rendered
7198             return;
7199         }
7200         this.el.removeClass(this.invalidClass);
7201         /*
7202         switch(this.msgTarget){
7203             case 'qtip':
7204                 this.el.dom.qtip = '';
7205                 break;
7206             case 'title':
7207                 this.el.dom.title = '';
7208                 break;
7209             case 'under':
7210                 if(this.errorEl){
7211                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
7212                 }
7213                 break;
7214             case 'side':
7215                 if(this.errorIcon){
7216                     this.errorIcon.dom.qtip = '';
7217                     this.errorIcon.hide();
7218                     this.un('resize', this.alignErrorIcon, this);
7219                 }
7220                 break;
7221             default:
7222                 var t = Roo.getDom(this.msgTarget);
7223                 t.innerHTML = '';
7224                 t.style.display = 'none';
7225                 break;
7226         }
7227         */
7228         this.fireEvent('valid', this);
7229     },
7230      /**
7231      * Mark this field as invalid
7232      * @param {String} msg The validation message
7233      */
7234     markInvalid : function(msg){
7235         if(!this.el  || this.preventMark){ // not rendered
7236             return;
7237         }
7238         this.el.addClass(this.invalidClass);
7239         /*
7240         msg = msg || this.invalidText;
7241         switch(this.msgTarget){
7242             case 'qtip':
7243                 this.el.dom.qtip = msg;
7244                 this.el.dom.qclass = 'x-form-invalid-tip';
7245                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
7246                     Roo.QuickTips.enable();
7247                 }
7248                 break;
7249             case 'title':
7250                 this.el.dom.title = msg;
7251                 break;
7252             case 'under':
7253                 if(!this.errorEl){
7254                     var elp = this.el.findParent('.x-form-element', 5, true);
7255                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
7256                     this.errorEl.setWidth(elp.getWidth(true)-20);
7257                 }
7258                 this.errorEl.update(msg);
7259                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
7260                 break;
7261             case 'side':
7262                 if(!this.errorIcon){
7263                     var elp = this.el.findParent('.x-form-element', 5, true);
7264                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
7265                 }
7266                 this.alignErrorIcon();
7267                 this.errorIcon.dom.qtip = msg;
7268                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
7269                 this.errorIcon.show();
7270                 this.on('resize', this.alignErrorIcon, this);
7271                 break;
7272             default:
7273                 var t = Roo.getDom(this.msgTarget);
7274                 t.innerHTML = msg;
7275                 t.style.display = this.msgDisplay;
7276                 break;
7277         }
7278         */
7279         this.fireEvent('invalid', this, msg);
7280     },
7281     // private
7282     SafariOnKeyDown : function(event)
7283     {
7284         // this is a workaround for a password hang bug on chrome/ webkit.
7285         
7286         var isSelectAll = false;
7287         
7288         if(this.inputEl().dom.selectionEnd > 0){
7289             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
7290         }
7291         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
7292             event.preventDefault();
7293             this.setValue('');
7294             return;
7295         }
7296         
7297         if(isSelectAll){ // backspace and delete key
7298             
7299             event.preventDefault();
7300             // this is very hacky as keydown always get's upper case.
7301             //
7302             var cc = String.fromCharCode(event.getCharCode());
7303             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
7304             
7305         }
7306     },
7307     adjustWidth : function(tag, w){
7308         tag = tag.toLowerCase();
7309         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
7310             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
7311                 if(tag == 'input'){
7312                     return w + 2;
7313                 }
7314                 if(tag == 'textarea'){
7315                     return w-2;
7316                 }
7317             }else if(Roo.isOpera){
7318                 if(tag == 'input'){
7319                     return w + 2;
7320                 }
7321                 if(tag == 'textarea'){
7322                     return w-2;
7323                 }
7324             }
7325         }
7326         return w;
7327     }
7328     
7329 });
7330
7331  
7332 /*
7333  * - LGPL
7334  *
7335  * Input
7336  * 
7337  */
7338
7339 /**
7340  * @class Roo.bootstrap.TextArea
7341  * @extends Roo.bootstrap.Input
7342  * Bootstrap TextArea class
7343  * @cfg {Number} cols Specifies the visible width of a text area
7344  * @cfg {Number} rows Specifies the visible number of lines in a text area
7345  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
7346  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
7347  * @cfg {string} html text
7348  * 
7349  * @constructor
7350  * Create a new TextArea
7351  * @param {Object} config The config object
7352  */
7353
7354 Roo.bootstrap.TextArea = function(config){
7355     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
7356    
7357 };
7358
7359 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
7360      
7361     cols : false,
7362     rows : 5,
7363     readOnly : false,
7364     warp : 'soft',
7365     resize : false,
7366     value: false,
7367     html: false,
7368     
7369     getAutoCreate : function(){
7370         
7371         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7372         
7373         var id = Roo.id();
7374         
7375         var cfg = {};
7376         
7377         var input =  {
7378             tag: 'textarea',
7379             id : id,
7380             warp : this.warp,
7381             rows : this.rows,
7382             value : this.value || '',
7383             html: this.html || '',
7384             cls : 'form-control',
7385             placeholder : this.placeholder || '' 
7386             
7387         };
7388         
7389         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7390             input.maxLength = this.maxLength;
7391         }
7392         
7393         if(this.resize){
7394             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
7395         }
7396         
7397         if(this.cols){
7398             input.cols = this.cols;
7399         }
7400         
7401         if (this.readOnly) {
7402             input.readonly = true;
7403         }
7404         
7405         if (this.name) {
7406             input.name = this.name;
7407         }
7408         
7409         if (this.size) {
7410             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
7411         }
7412         
7413         var settings=this;
7414         ['xs','sm','md','lg'].map(function(size){
7415             if (settings[size]) {
7416                 cfg.cls += ' col-' + size + '-' + settings[size];
7417             }
7418         });
7419         
7420         var inputblock = input;
7421         
7422         if (this.before || this.after) {
7423             
7424             inputblock = {
7425                 cls : 'input-group',
7426                 cn :  [] 
7427             };
7428             if (this.before) {
7429                 inputblock.cn.push({
7430                     tag :'span',
7431                     cls : 'input-group-addon',
7432                     html : this.before
7433                 });
7434             }
7435             inputblock.cn.push(input);
7436             if (this.after) {
7437                 inputblock.cn.push({
7438                     tag :'span',
7439                     cls : 'input-group-addon',
7440                     html : this.after
7441                 });
7442             }
7443             
7444         }
7445         
7446         if (align ==='left' && this.fieldLabel.length) {
7447                 Roo.log("left and has label");
7448                 cfg.cn = [
7449                     
7450                     {
7451                         tag: 'label',
7452                         'for' :  id,
7453                         cls : 'control-label col-sm-' + this.labelWidth,
7454                         html : this.fieldLabel
7455                         
7456                     },
7457                     {
7458                         cls : "col-sm-" + (12 - this.labelWidth), 
7459                         cn: [
7460                             inputblock
7461                         ]
7462                     }
7463                     
7464                 ];
7465         } else if ( this.fieldLabel.length) {
7466                 Roo.log(" label");
7467                  cfg.cn = [
7468                    
7469                     {
7470                         tag: 'label',
7471                         //cls : 'input-group-addon',
7472                         html : this.fieldLabel
7473                         
7474                     },
7475                     
7476                     inputblock
7477                     
7478                 ];
7479
7480         } else {
7481             
7482                    Roo.log(" no label && no align");
7483                 cfg.cn = [
7484                     
7485                         inputblock
7486                     
7487                 ];
7488                 
7489                 
7490         }
7491         
7492         if (this.disabled) {
7493             input.disabled=true;
7494         }
7495         
7496         return cfg;
7497         
7498     },
7499     /**
7500      * return the real textarea element.
7501      */
7502     inputEl: function ()
7503     {
7504         return this.el.select('textarea.form-control',true).first();
7505     }
7506 });
7507
7508  
7509 /*
7510  * - LGPL
7511  *
7512  * trigger field - base class for combo..
7513  * 
7514  */
7515  
7516 /**
7517  * @class Roo.bootstrap.TriggerField
7518  * @extends Roo.bootstrap.Input
7519  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
7520  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
7521  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
7522  * for which you can provide a custom implementation.  For example:
7523  * <pre><code>
7524 var trigger = new Roo.bootstrap.TriggerField();
7525 trigger.onTriggerClick = myTriggerFn;
7526 trigger.applyTo('my-field');
7527 </code></pre>
7528  *
7529  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
7530  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
7531  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
7532  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
7533  * @constructor
7534  * Create a new TriggerField.
7535  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
7536  * to the base TextField)
7537  */
7538 Roo.bootstrap.TriggerField = function(config){
7539     this.mimicing = false;
7540     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
7541 };
7542
7543 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
7544     /**
7545      * @cfg {String} triggerClass A CSS class to apply to the trigger
7546      */
7547      /**
7548      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
7549      */
7550     hideTrigger:false,
7551
7552     /** @cfg {Boolean} grow @hide */
7553     /** @cfg {Number} growMin @hide */
7554     /** @cfg {Number} growMax @hide */
7555
7556     /**
7557      * @hide 
7558      * @method
7559      */
7560     autoSize: Roo.emptyFn,
7561     // private
7562     monitorTab : true,
7563     // private
7564     deferHeight : true,
7565
7566     
7567     actionMode : 'wrap',
7568     
7569     
7570     
7571     getAutoCreate : function(){
7572        
7573         var parent = this.parent();
7574         
7575         var align = this.labelAlign || this.parentLabelAlign();
7576         
7577         var id = Roo.id();
7578         
7579         var cfg = {
7580             cls: 'form-group' //input-group
7581         };
7582         
7583         
7584         var input =  {
7585             tag: 'input',
7586             id : id,
7587             type : this.inputType,
7588             cls : 'form-control',
7589             autocomplete: 'off',
7590             placeholder : this.placeholder || '' 
7591             
7592         };
7593         if (this.name) {
7594             input.name = this.name;
7595         }
7596         if (this.size) {
7597             input.cls += ' input-' + this.size;
7598         }
7599         
7600         if (this.disabled) {
7601             input.disabled=true;
7602         }
7603         
7604         var inputblock = input;
7605         
7606         if (this.before || this.after) {
7607             
7608             inputblock = {
7609                 cls : 'input-group',
7610                 cn :  [] 
7611             };
7612             if (this.before) {
7613                 inputblock.cn.push({
7614                     tag :'span',
7615                     cls : 'input-group-addon',
7616                     html : this.before
7617                 });
7618             }
7619             inputblock.cn.push(input);
7620             if (this.after) {
7621                 inputblock.cn.push({
7622                     tag :'span',
7623                     cls : 'input-group-addon',
7624                     html : this.after
7625                 });
7626             }
7627             
7628         };
7629         
7630         var box = {
7631             tag: 'div',
7632             cn: [
7633                 {
7634                     tag: 'input',
7635                     type : 'hidden',
7636                     cls: 'form-hidden-field'
7637                 },
7638                 inputblock
7639             ]
7640             
7641         };
7642         
7643         if(this.multiple){
7644             Roo.log('multiple');
7645             
7646             box = {
7647                 tag: 'div',
7648                 cn: [
7649                     {
7650                         tag: 'input',
7651                         type : 'hidden',
7652                         cls: 'form-hidden-field'
7653                     },
7654                     {
7655                         tag: 'ul',
7656                         cls: 'select2-choices',
7657                         cn:[
7658                             {
7659                                 tag: 'li',
7660                                 cls: 'select2-search-field',
7661                                 cn: [
7662
7663                                     inputblock
7664                                 ]
7665                             }
7666                         ]
7667                     }
7668                 ]
7669             }
7670         };
7671         
7672         var combobox = {
7673             cls: 'select2-container input-group',
7674             cn: [
7675                 box,
7676                 {
7677                     tag: 'ul',
7678                     cls: 'typeahead typeahead-long dropdown-menu',
7679                     style: 'display:none'
7680                 }
7681             ]
7682         };
7683         
7684         if(!this.multiple){
7685             combobox.cn.push({
7686                 tag :'span',
7687                 cls : 'input-group-addon btn dropdown-toggle',
7688                 cn : [
7689                     {
7690                         tag: 'span',
7691                         cls: 'caret'
7692                     },
7693                     {
7694                         tag: 'span',
7695                         cls: 'combobox-clear',
7696                         cn  : [
7697                             {
7698                                 tag : 'i',
7699                                 cls: 'icon-remove'
7700                             }
7701                         ]
7702                     }
7703                 ]
7704
7705             })
7706         }
7707         
7708         if(this.multiple){
7709             combobox.cls += ' select2-container-multi';
7710         }
7711         
7712         if (align ==='left' && this.fieldLabel.length) {
7713             
7714                 Roo.log("left and has label");
7715                 cfg.cn = [
7716                     
7717                     {
7718                         tag: 'label',
7719                         'for' :  id,
7720                         cls : 'control-label col-sm-' + this.labelWidth,
7721                         html : this.fieldLabel
7722                         
7723                     },
7724                     {
7725                         cls : "col-sm-" + (12 - this.labelWidth), 
7726                         cn: [
7727                             combobox
7728                         ]
7729                     }
7730                     
7731                 ];
7732         } else if ( this.fieldLabel.length) {
7733                 Roo.log(" label");
7734                  cfg.cn = [
7735                    
7736                     {
7737                         tag: 'label',
7738                         //cls : 'input-group-addon',
7739                         html : this.fieldLabel
7740                         
7741                     },
7742                     
7743                     combobox
7744                     
7745                 ];
7746
7747         } else {
7748             
7749                 Roo.log(" no label && no align");
7750                 cfg = combobox
7751                      
7752                 
7753         }
7754          
7755         var settings=this;
7756         ['xs','sm','md','lg'].map(function(size){
7757             if (settings[size]) {
7758                 cfg.cls += ' col-' + size + '-' + settings[size];
7759             }
7760         });
7761         
7762         return cfg;
7763         
7764     },
7765     
7766     
7767     
7768     // private
7769     onResize : function(w, h){
7770 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
7771 //        if(typeof w == 'number'){
7772 //            var x = w - this.trigger.getWidth();
7773 //            this.inputEl().setWidth(this.adjustWidth('input', x));
7774 //            this.trigger.setStyle('left', x+'px');
7775 //        }
7776     },
7777
7778     // private
7779     adjustSize : Roo.BoxComponent.prototype.adjustSize,
7780
7781     // private
7782     getResizeEl : function(){
7783         return this.inputEl();
7784     },
7785
7786     // private
7787     getPositionEl : function(){
7788         return this.inputEl();
7789     },
7790
7791     // private
7792     alignErrorIcon : function(){
7793         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
7794     },
7795
7796     // private
7797     initEvents : function(){
7798         
7799         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
7800         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
7801         if(!this.multiple){
7802             this.trigger = this.el.select('span.dropdown-toggle',true).first();
7803             if(this.hideTrigger){
7804                 this.trigger.setDisplayed(false);
7805             }
7806             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
7807         }
7808         
7809         if(this.multiple){
7810             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
7811         }
7812         
7813         //this.trigger.addClassOnOver('x-form-trigger-over');
7814         //this.trigger.addClassOnClick('x-form-trigger-click');
7815         
7816         //if(!this.width){
7817         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
7818         //}
7819     },
7820
7821     // private
7822     initTrigger : function(){
7823        
7824     },
7825
7826     // private
7827     onDestroy : function(){
7828         if(this.trigger){
7829             this.trigger.removeAllListeners();
7830           //  this.trigger.remove();
7831         }
7832         //if(this.wrap){
7833         //    this.wrap.remove();
7834         //}
7835         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
7836     },
7837
7838     // private
7839     onFocus : function(){
7840         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
7841         /*
7842         if(!this.mimicing){
7843             this.wrap.addClass('x-trigger-wrap-focus');
7844             this.mimicing = true;
7845             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
7846             if(this.monitorTab){
7847                 this.el.on("keydown", this.checkTab, this);
7848             }
7849         }
7850         */
7851     },
7852
7853     // private
7854     checkTab : function(e){
7855         if(e.getKey() == e.TAB){
7856             this.triggerBlur();
7857         }
7858     },
7859
7860     // private
7861     onBlur : function(){
7862         // do nothing
7863     },
7864
7865     // private
7866     mimicBlur : function(e, t){
7867         /*
7868         if(!this.wrap.contains(t) && this.validateBlur()){
7869             this.triggerBlur();
7870         }
7871         */
7872     },
7873
7874     // private
7875     triggerBlur : function(){
7876         this.mimicing = false;
7877         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
7878         if(this.monitorTab){
7879             this.el.un("keydown", this.checkTab, this);
7880         }
7881         //this.wrap.removeClass('x-trigger-wrap-focus');
7882         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
7883     },
7884
7885     // private
7886     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
7887     validateBlur : function(e, t){
7888         return true;
7889     },
7890
7891     // private
7892     onDisable : function(){
7893         this.inputEl().dom.disabled = true;
7894         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
7895         //if(this.wrap){
7896         //    this.wrap.addClass('x-item-disabled');
7897         //}
7898     },
7899
7900     // private
7901     onEnable : function(){
7902         this.inputEl().dom.disabled = false;
7903         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
7904         //if(this.wrap){
7905         //    this.el.removeClass('x-item-disabled');
7906         //}
7907     },
7908
7909     // private
7910     onShow : function(){
7911         var ae = this.getActionEl();
7912         
7913         if(ae){
7914             ae.dom.style.display = '';
7915             ae.dom.style.visibility = 'visible';
7916         }
7917     },
7918
7919     // private
7920     
7921     onHide : function(){
7922         var ae = this.getActionEl();
7923         ae.dom.style.display = 'none';
7924     },
7925
7926     /**
7927      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
7928      * by an implementing function.
7929      * @method
7930      * @param {EventObject} e
7931      */
7932     onTriggerClick : Roo.emptyFn
7933 });
7934  /*
7935  * Based on:
7936  * Ext JS Library 1.1.1
7937  * Copyright(c) 2006-2007, Ext JS, LLC.
7938  *
7939  * Originally Released Under LGPL - original licence link has changed is not relivant.
7940  *
7941  * Fork - LGPL
7942  * <script type="text/javascript">
7943  */
7944
7945
7946 /**
7947  * @class Roo.data.SortTypes
7948  * @singleton
7949  * Defines the default sorting (casting?) comparison functions used when sorting data.
7950  */
7951 Roo.data.SortTypes = {
7952     /**
7953      * Default sort that does nothing
7954      * @param {Mixed} s The value being converted
7955      * @return {Mixed} The comparison value
7956      */
7957     none : function(s){
7958         return s;
7959     },
7960     
7961     /**
7962      * The regular expression used to strip tags
7963      * @type {RegExp}
7964      * @property
7965      */
7966     stripTagsRE : /<\/?[^>]+>/gi,
7967     
7968     /**
7969      * Strips all HTML tags to sort on text only
7970      * @param {Mixed} s The value being converted
7971      * @return {String} The comparison value
7972      */
7973     asText : function(s){
7974         return String(s).replace(this.stripTagsRE, "");
7975     },
7976     
7977     /**
7978      * Strips all HTML tags to sort on text only - Case insensitive
7979      * @param {Mixed} s The value being converted
7980      * @return {String} The comparison value
7981      */
7982     asUCText : function(s){
7983         return String(s).toUpperCase().replace(this.stripTagsRE, "");
7984     },
7985     
7986     /**
7987      * Case insensitive string
7988      * @param {Mixed} s The value being converted
7989      * @return {String} The comparison value
7990      */
7991     asUCString : function(s) {
7992         return String(s).toUpperCase();
7993     },
7994     
7995     /**
7996      * Date sorting
7997      * @param {Mixed} s The value being converted
7998      * @return {Number} The comparison value
7999      */
8000     asDate : function(s) {
8001         if(!s){
8002             return 0;
8003         }
8004         if(s instanceof Date){
8005             return s.getTime();
8006         }
8007         return Date.parse(String(s));
8008     },
8009     
8010     /**
8011      * Float sorting
8012      * @param {Mixed} s The value being converted
8013      * @return {Float} The comparison value
8014      */
8015     asFloat : function(s) {
8016         var val = parseFloat(String(s).replace(/,/g, ""));
8017         if(isNaN(val)) val = 0;
8018         return val;
8019     },
8020     
8021     /**
8022      * Integer sorting
8023      * @param {Mixed} s The value being converted
8024      * @return {Number} The comparison value
8025      */
8026     asInt : function(s) {
8027         var val = parseInt(String(s).replace(/,/g, ""));
8028         if(isNaN(val)) val = 0;
8029         return val;
8030     }
8031 };/*
8032  * Based on:
8033  * Ext JS Library 1.1.1
8034  * Copyright(c) 2006-2007, Ext JS, LLC.
8035  *
8036  * Originally Released Under LGPL - original licence link has changed is not relivant.
8037  *
8038  * Fork - LGPL
8039  * <script type="text/javascript">
8040  */
8041
8042 /**
8043 * @class Roo.data.Record
8044  * Instances of this class encapsulate both record <em>definition</em> information, and record
8045  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
8046  * to access Records cached in an {@link Roo.data.Store} object.<br>
8047  * <p>
8048  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
8049  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
8050  * objects.<br>
8051  * <p>
8052  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
8053  * @constructor
8054  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
8055  * {@link #create}. The parameters are the same.
8056  * @param {Array} data An associative Array of data values keyed by the field name.
8057  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
8058  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
8059  * not specified an integer id is generated.
8060  */
8061 Roo.data.Record = function(data, id){
8062     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
8063     this.data = data;
8064 };
8065
8066 /**
8067  * Generate a constructor for a specific record layout.
8068  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
8069  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
8070  * Each field definition object may contain the following properties: <ul>
8071  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
8072  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
8073  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
8074  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
8075  * is being used, then this is a string containing the javascript expression to reference the data relative to 
8076  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
8077  * to the data item relative to the record element. If the mapping expression is the same as the field name,
8078  * this may be omitted.</p></li>
8079  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
8080  * <ul><li>auto (Default, implies no conversion)</li>
8081  * <li>string</li>
8082  * <li>int</li>
8083  * <li>float</li>
8084  * <li>boolean</li>
8085  * <li>date</li></ul></p></li>
8086  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
8087  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
8088  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
8089  * by the Reader into an object that will be stored in the Record. It is passed the
8090  * following parameters:<ul>
8091  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
8092  * </ul></p></li>
8093  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
8094  * </ul>
8095  * <br>usage:<br><pre><code>
8096 var TopicRecord = Roo.data.Record.create(
8097     {name: 'title', mapping: 'topic_title'},
8098     {name: 'author', mapping: 'username'},
8099     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
8100     {name: 'lastPost', mapping: 'post_time', type: 'date'},
8101     {name: 'lastPoster', mapping: 'user2'},
8102     {name: 'excerpt', mapping: 'post_text'}
8103 );
8104
8105 var myNewRecord = new TopicRecord({
8106     title: 'Do my job please',
8107     author: 'noobie',
8108     totalPosts: 1,
8109     lastPost: new Date(),
8110     lastPoster: 'Animal',
8111     excerpt: 'No way dude!'
8112 });
8113 myStore.add(myNewRecord);
8114 </code></pre>
8115  * @method create
8116  * @static
8117  */
8118 Roo.data.Record.create = function(o){
8119     var f = function(){
8120         f.superclass.constructor.apply(this, arguments);
8121     };
8122     Roo.extend(f, Roo.data.Record);
8123     var p = f.prototype;
8124     p.fields = new Roo.util.MixedCollection(false, function(field){
8125         return field.name;
8126     });
8127     for(var i = 0, len = o.length; i < len; i++){
8128         p.fields.add(new Roo.data.Field(o[i]));
8129     }
8130     f.getField = function(name){
8131         return p.fields.get(name);  
8132     };
8133     return f;
8134 };
8135
8136 Roo.data.Record.AUTO_ID = 1000;
8137 Roo.data.Record.EDIT = 'edit';
8138 Roo.data.Record.REJECT = 'reject';
8139 Roo.data.Record.COMMIT = 'commit';
8140
8141 Roo.data.Record.prototype = {
8142     /**
8143      * Readonly flag - true if this record has been modified.
8144      * @type Boolean
8145      */
8146     dirty : false,
8147     editing : false,
8148     error: null,
8149     modified: null,
8150
8151     // private
8152     join : function(store){
8153         this.store = store;
8154     },
8155
8156     /**
8157      * Set the named field to the specified value.
8158      * @param {String} name The name of the field to set.
8159      * @param {Object} value The value to set the field to.
8160      */
8161     set : function(name, value){
8162         if(this.data[name] == value){
8163             return;
8164         }
8165         this.dirty = true;
8166         if(!this.modified){
8167             this.modified = {};
8168         }
8169         if(typeof this.modified[name] == 'undefined'){
8170             this.modified[name] = this.data[name];
8171         }
8172         this.data[name] = value;
8173         if(!this.editing && this.store){
8174             this.store.afterEdit(this);
8175         }       
8176     },
8177
8178     /**
8179      * Get the value of the named field.
8180      * @param {String} name The name of the field to get the value of.
8181      * @return {Object} The value of the field.
8182      */
8183     get : function(name){
8184         return this.data[name]; 
8185     },
8186
8187     // private
8188     beginEdit : function(){
8189         this.editing = true;
8190         this.modified = {}; 
8191     },
8192
8193     // private
8194     cancelEdit : function(){
8195         this.editing = false;
8196         delete this.modified;
8197     },
8198
8199     // private
8200     endEdit : function(){
8201         this.editing = false;
8202         if(this.dirty && this.store){
8203             this.store.afterEdit(this);
8204         }
8205     },
8206
8207     /**
8208      * Usually called by the {@link Roo.data.Store} which owns the Record.
8209      * Rejects all changes made to the Record since either creation, or the last commit operation.
8210      * Modified fields are reverted to their original values.
8211      * <p>
8212      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8213      * of reject operations.
8214      */
8215     reject : function(){
8216         var m = this.modified;
8217         for(var n in m){
8218             if(typeof m[n] != "function"){
8219                 this.data[n] = m[n];
8220             }
8221         }
8222         this.dirty = false;
8223         delete this.modified;
8224         this.editing = false;
8225         if(this.store){
8226             this.store.afterReject(this);
8227         }
8228     },
8229
8230     /**
8231      * Usually called by the {@link Roo.data.Store} which owns the Record.
8232      * Commits all changes made to the Record since either creation, or the last commit operation.
8233      * <p>
8234      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8235      * of commit operations.
8236      */
8237     commit : function(){
8238         this.dirty = false;
8239         delete this.modified;
8240         this.editing = false;
8241         if(this.store){
8242             this.store.afterCommit(this);
8243         }
8244     },
8245
8246     // private
8247     hasError : function(){
8248         return this.error != null;
8249     },
8250
8251     // private
8252     clearError : function(){
8253         this.error = null;
8254     },
8255
8256     /**
8257      * Creates a copy of this record.
8258      * @param {String} id (optional) A new record id if you don't want to use this record's id
8259      * @return {Record}
8260      */
8261     copy : function(newId) {
8262         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
8263     }
8264 };/*
8265  * Based on:
8266  * Ext JS Library 1.1.1
8267  * Copyright(c) 2006-2007, Ext JS, LLC.
8268  *
8269  * Originally Released Under LGPL - original licence link has changed is not relivant.
8270  *
8271  * Fork - LGPL
8272  * <script type="text/javascript">
8273  */
8274
8275
8276
8277 /**
8278  * @class Roo.data.Store
8279  * @extends Roo.util.Observable
8280  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
8281  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
8282  * <p>
8283  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
8284  * has no knowledge of the format of the data returned by the Proxy.<br>
8285  * <p>
8286  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
8287  * instances from the data object. These records are cached and made available through accessor functions.
8288  * @constructor
8289  * Creates a new Store.
8290  * @param {Object} config A config object containing the objects needed for the Store to access data,
8291  * and read the data into Records.
8292  */
8293 Roo.data.Store = function(config){
8294     this.data = new Roo.util.MixedCollection(false);
8295     this.data.getKey = function(o){
8296         return o.id;
8297     };
8298     this.baseParams = {};
8299     // private
8300     this.paramNames = {
8301         "start" : "start",
8302         "limit" : "limit",
8303         "sort" : "sort",
8304         "dir" : "dir",
8305         "multisort" : "_multisort"
8306     };
8307
8308     if(config && config.data){
8309         this.inlineData = config.data;
8310         delete config.data;
8311     }
8312
8313     Roo.apply(this, config);
8314     
8315     if(this.reader){ // reader passed
8316         this.reader = Roo.factory(this.reader, Roo.data);
8317         this.reader.xmodule = this.xmodule || false;
8318         if(!this.recordType){
8319             this.recordType = this.reader.recordType;
8320         }
8321         if(this.reader.onMetaChange){
8322             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
8323         }
8324     }
8325
8326     if(this.recordType){
8327         this.fields = this.recordType.prototype.fields;
8328     }
8329     this.modified = [];
8330
8331     this.addEvents({
8332         /**
8333          * @event datachanged
8334          * Fires when the data cache has changed, and a widget which is using this Store
8335          * as a Record cache should refresh its view.
8336          * @param {Store} this
8337          */
8338         datachanged : true,
8339         /**
8340          * @event metachange
8341          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
8342          * @param {Store} this
8343          * @param {Object} meta The JSON metadata
8344          */
8345         metachange : true,
8346         /**
8347          * @event add
8348          * Fires when Records have been added to the Store
8349          * @param {Store} this
8350          * @param {Roo.data.Record[]} records The array of Records added
8351          * @param {Number} index The index at which the record(s) were added
8352          */
8353         add : true,
8354         /**
8355          * @event remove
8356          * Fires when a Record has been removed from the Store
8357          * @param {Store} this
8358          * @param {Roo.data.Record} record The Record that was removed
8359          * @param {Number} index The index at which the record was removed
8360          */
8361         remove : true,
8362         /**
8363          * @event update
8364          * Fires when a Record has been updated
8365          * @param {Store} this
8366          * @param {Roo.data.Record} record The Record that was updated
8367          * @param {String} operation The update operation being performed.  Value may be one of:
8368          * <pre><code>
8369  Roo.data.Record.EDIT
8370  Roo.data.Record.REJECT
8371  Roo.data.Record.COMMIT
8372          * </code></pre>
8373          */
8374         update : true,
8375         /**
8376          * @event clear
8377          * Fires when the data cache has been cleared.
8378          * @param {Store} this
8379          */
8380         clear : true,
8381         /**
8382          * @event beforeload
8383          * Fires before a request is made for a new data object.  If the beforeload handler returns false
8384          * the load action will be canceled.
8385          * @param {Store} this
8386          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8387          */
8388         beforeload : true,
8389         /**
8390          * @event beforeloadadd
8391          * Fires after a new set of Records has been loaded.
8392          * @param {Store} this
8393          * @param {Roo.data.Record[]} records The Records that were loaded
8394          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8395          */
8396         beforeloadadd : true,
8397         /**
8398          * @event load
8399          * Fires after a new set of Records has been loaded, before they are added to the store.
8400          * @param {Store} this
8401          * @param {Roo.data.Record[]} records The Records that were loaded
8402          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8403          * @params {Object} return from reader
8404          */
8405         load : true,
8406         /**
8407          * @event loadexception
8408          * Fires if an exception occurs in the Proxy during loading.
8409          * Called with the signature of the Proxy's "loadexception" event.
8410          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
8411          * 
8412          * @param {Proxy} 
8413          * @param {Object} return from JsonData.reader() - success, totalRecords, records
8414          * @param {Object} load options 
8415          * @param {Object} jsonData from your request (normally this contains the Exception)
8416          */
8417         loadexception : true
8418     });
8419     
8420     if(this.proxy){
8421         this.proxy = Roo.factory(this.proxy, Roo.data);
8422         this.proxy.xmodule = this.xmodule || false;
8423         this.relayEvents(this.proxy,  ["loadexception"]);
8424     }
8425     this.sortToggle = {};
8426     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
8427
8428     Roo.data.Store.superclass.constructor.call(this);
8429
8430     if(this.inlineData){
8431         this.loadData(this.inlineData);
8432         delete this.inlineData;
8433     }
8434 };
8435
8436 Roo.extend(Roo.data.Store, Roo.util.Observable, {
8437      /**
8438     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
8439     * without a remote query - used by combo/forms at present.
8440     */
8441     
8442     /**
8443     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
8444     */
8445     /**
8446     * @cfg {Array} data Inline data to be loaded when the store is initialized.
8447     */
8448     /**
8449     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
8450     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
8451     */
8452     /**
8453     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
8454     * on any HTTP request
8455     */
8456     /**
8457     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
8458     */
8459     /**
8460     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
8461     */
8462     multiSort: false,
8463     /**
8464     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
8465     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
8466     */
8467     remoteSort : false,
8468
8469     /**
8470     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
8471      * loaded or when a record is removed. (defaults to false).
8472     */
8473     pruneModifiedRecords : false,
8474
8475     // private
8476     lastOptions : null,
8477
8478     /**
8479      * Add Records to the Store and fires the add event.
8480      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8481      */
8482     add : function(records){
8483         records = [].concat(records);
8484         for(var i = 0, len = records.length; i < len; i++){
8485             records[i].join(this);
8486         }
8487         var index = this.data.length;
8488         this.data.addAll(records);
8489         this.fireEvent("add", this, records, index);
8490     },
8491
8492     /**
8493      * Remove a Record from the Store and fires the remove event.
8494      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
8495      */
8496     remove : function(record){
8497         var index = this.data.indexOf(record);
8498         this.data.removeAt(index);
8499         if(this.pruneModifiedRecords){
8500             this.modified.remove(record);
8501         }
8502         this.fireEvent("remove", this, record, index);
8503     },
8504
8505     /**
8506      * Remove all Records from the Store and fires the clear event.
8507      */
8508     removeAll : function(){
8509         this.data.clear();
8510         if(this.pruneModifiedRecords){
8511             this.modified = [];
8512         }
8513         this.fireEvent("clear", this);
8514     },
8515
8516     /**
8517      * Inserts Records to the Store at the given index and fires the add event.
8518      * @param {Number} index The start index at which to insert the passed Records.
8519      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8520      */
8521     insert : function(index, records){
8522         records = [].concat(records);
8523         for(var i = 0, len = records.length; i < len; i++){
8524             this.data.insert(index, records[i]);
8525             records[i].join(this);
8526         }
8527         this.fireEvent("add", this, records, index);
8528     },
8529
8530     /**
8531      * Get the index within the cache of the passed Record.
8532      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
8533      * @return {Number} The index of the passed Record. Returns -1 if not found.
8534      */
8535     indexOf : function(record){
8536         return this.data.indexOf(record);
8537     },
8538
8539     /**
8540      * Get the index within the cache of the Record with the passed id.
8541      * @param {String} id The id of the Record to find.
8542      * @return {Number} The index of the Record. Returns -1 if not found.
8543      */
8544     indexOfId : function(id){
8545         return this.data.indexOfKey(id);
8546     },
8547
8548     /**
8549      * Get the Record with the specified id.
8550      * @param {String} id The id of the Record to find.
8551      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
8552      */
8553     getById : function(id){
8554         return this.data.key(id);
8555     },
8556
8557     /**
8558      * Get the Record at the specified index.
8559      * @param {Number} index The index of the Record to find.
8560      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
8561      */
8562     getAt : function(index){
8563         return this.data.itemAt(index);
8564     },
8565
8566     /**
8567      * Returns a range of Records between specified indices.
8568      * @param {Number} startIndex (optional) The starting index (defaults to 0)
8569      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
8570      * @return {Roo.data.Record[]} An array of Records
8571      */
8572     getRange : function(start, end){
8573         return this.data.getRange(start, end);
8574     },
8575
8576     // private
8577     storeOptions : function(o){
8578         o = Roo.apply({}, o);
8579         delete o.callback;
8580         delete o.scope;
8581         this.lastOptions = o;
8582     },
8583
8584     /**
8585      * Loads the Record cache from the configured Proxy using the configured Reader.
8586      * <p>
8587      * If using remote paging, then the first load call must specify the <em>start</em>
8588      * and <em>limit</em> properties in the options.params property to establish the initial
8589      * position within the dataset, and the number of Records to cache on each read from the Proxy.
8590      * <p>
8591      * <strong>It is important to note that for remote data sources, loading is asynchronous,
8592      * and this call will return before the new data has been loaded. Perform any post-processing
8593      * in a callback function, or in a "load" event handler.</strong>
8594      * <p>
8595      * @param {Object} options An object containing properties which control loading options:<ul>
8596      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
8597      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
8598      * passed the following arguments:<ul>
8599      * <li>r : Roo.data.Record[]</li>
8600      * <li>options: Options object from the load call</li>
8601      * <li>success: Boolean success indicator</li></ul></li>
8602      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
8603      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
8604      * </ul>
8605      */
8606     load : function(options){
8607         options = options || {};
8608         if(this.fireEvent("beforeload", this, options) !== false){
8609             this.storeOptions(options);
8610             var p = Roo.apply(options.params || {}, this.baseParams);
8611             // if meta was not loaded from remote source.. try requesting it.
8612             if (!this.reader.metaFromRemote) {
8613                 p._requestMeta = 1;
8614             }
8615             if(this.sortInfo && this.remoteSort){
8616                 var pn = this.paramNames;
8617                 p[pn["sort"]] = this.sortInfo.field;
8618                 p[pn["dir"]] = this.sortInfo.direction;
8619             }
8620             if (this.multiSort) {
8621                 var pn = this.paramNames;
8622                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
8623             }
8624             
8625             this.proxy.load(p, this.reader, this.loadRecords, this, options);
8626         }
8627     },
8628
8629     /**
8630      * Reloads the Record cache from the configured Proxy using the configured Reader and
8631      * the options from the last load operation performed.
8632      * @param {Object} options (optional) An object containing properties which may override the options
8633      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
8634      * the most recently used options are reused).
8635      */
8636     reload : function(options){
8637         this.load(Roo.applyIf(options||{}, this.lastOptions));
8638     },
8639
8640     // private
8641     // Called as a callback by the Reader during a load operation.
8642     loadRecords : function(o, options, success){
8643         if(!o || success === false){
8644             if(success !== false){
8645                 this.fireEvent("load", this, [], options, o);
8646             }
8647             if(options.callback){
8648                 options.callback.call(options.scope || this, [], options, false);
8649             }
8650             return;
8651         }
8652         // if data returned failure - throw an exception.
8653         if (o.success === false) {
8654             // show a message if no listener is registered.
8655             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
8656                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
8657             }
8658             // loadmask wil be hooked into this..
8659             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
8660             return;
8661         }
8662         var r = o.records, t = o.totalRecords || r.length;
8663         
8664         this.fireEvent("beforeloadadd", this, r, options, o);
8665         
8666         if(!options || options.add !== true){
8667             if(this.pruneModifiedRecords){
8668                 this.modified = [];
8669             }
8670             for(var i = 0, len = r.length; i < len; i++){
8671                 r[i].join(this);
8672             }
8673             if(this.snapshot){
8674                 this.data = this.snapshot;
8675                 delete this.snapshot;
8676             }
8677             this.data.clear();
8678             this.data.addAll(r);
8679             this.totalLength = t;
8680             this.applySort();
8681             this.fireEvent("datachanged", this);
8682         }else{
8683             this.totalLength = Math.max(t, this.data.length+r.length);
8684             this.add(r);
8685         }
8686         this.fireEvent("load", this, r, options, o);
8687         if(options.callback){
8688             options.callback.call(options.scope || this, r, options, true);
8689         }
8690     },
8691
8692
8693     /**
8694      * Loads data from a passed data block. A Reader which understands the format of the data
8695      * must have been configured in the constructor.
8696      * @param {Object} data The data block from which to read the Records.  The format of the data expected
8697      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
8698      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
8699      */
8700     loadData : function(o, append){
8701         var r = this.reader.readRecords(o);
8702         this.loadRecords(r, {add: append}, true);
8703     },
8704
8705     /**
8706      * Gets the number of cached records.
8707      * <p>
8708      * <em>If using paging, this may not be the total size of the dataset. If the data object
8709      * used by the Reader contains the dataset size, then the getTotalCount() function returns
8710      * the data set size</em>
8711      */
8712     getCount : function(){
8713         return this.data.length || 0;
8714     },
8715
8716     /**
8717      * Gets the total number of records in the dataset as returned by the server.
8718      * <p>
8719      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
8720      * the dataset size</em>
8721      */
8722     getTotalCount : function(){
8723         return this.totalLength || 0;
8724     },
8725
8726     /**
8727      * Returns the sort state of the Store as an object with two properties:
8728      * <pre><code>
8729  field {String} The name of the field by which the Records are sorted
8730  direction {String} The sort order, "ASC" or "DESC"
8731      * </code></pre>
8732      */
8733     getSortState : function(){
8734         return this.sortInfo;
8735     },
8736
8737     // private
8738     applySort : function(){
8739         if(this.sortInfo && !this.remoteSort){
8740             var s = this.sortInfo, f = s.field;
8741             var st = this.fields.get(f).sortType;
8742             var fn = function(r1, r2){
8743                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
8744                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
8745             };
8746             this.data.sort(s.direction, fn);
8747             if(this.snapshot && this.snapshot != this.data){
8748                 this.snapshot.sort(s.direction, fn);
8749             }
8750         }
8751     },
8752
8753     /**
8754      * Sets the default sort column and order to be used by the next load operation.
8755      * @param {String} fieldName The name of the field to sort by.
8756      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
8757      */
8758     setDefaultSort : function(field, dir){
8759         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
8760     },
8761
8762     /**
8763      * Sort the Records.
8764      * If remote sorting is used, the sort is performed on the server, and the cache is
8765      * reloaded. If local sorting is used, the cache is sorted internally.
8766      * @param {String} fieldName The name of the field to sort by.
8767      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
8768      */
8769     sort : function(fieldName, dir){
8770         var f = this.fields.get(fieldName);
8771         if(!dir){
8772             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
8773             
8774             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
8775                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
8776             }else{
8777                 dir = f.sortDir;
8778             }
8779         }
8780         this.sortToggle[f.name] = dir;
8781         this.sortInfo = {field: f.name, direction: dir};
8782         if(!this.remoteSort){
8783             this.applySort();
8784             this.fireEvent("datachanged", this);
8785         }else{
8786             this.load(this.lastOptions);
8787         }
8788     },
8789
8790     /**
8791      * Calls the specified function for each of the Records in the cache.
8792      * @param {Function} fn The function to call. The Record is passed as the first parameter.
8793      * Returning <em>false</em> aborts and exits the iteration.
8794      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
8795      */
8796     each : function(fn, scope){
8797         this.data.each(fn, scope);
8798     },
8799
8800     /**
8801      * Gets all records modified since the last commit.  Modified records are persisted across load operations
8802      * (e.g., during paging).
8803      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
8804      */
8805     getModifiedRecords : function(){
8806         return this.modified;
8807     },
8808
8809     // private
8810     createFilterFn : function(property, value, anyMatch){
8811         if(!value.exec){ // not a regex
8812             value = String(value);
8813             if(value.length == 0){
8814                 return false;
8815             }
8816             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
8817         }
8818         return function(r){
8819             return value.test(r.data[property]);
8820         };
8821     },
8822
8823     /**
8824      * Sums the value of <i>property</i> for each record between start and end and returns the result.
8825      * @param {String} property A field on your records
8826      * @param {Number} start The record index to start at (defaults to 0)
8827      * @param {Number} end The last record index to include (defaults to length - 1)
8828      * @return {Number} The sum
8829      */
8830     sum : function(property, start, end){
8831         var rs = this.data.items, v = 0;
8832         start = start || 0;
8833         end = (end || end === 0) ? end : rs.length-1;
8834
8835         for(var i = start; i <= end; i++){
8836             v += (rs[i].data[property] || 0);
8837         }
8838         return v;
8839     },
8840
8841     /**
8842      * Filter the records by a specified property.
8843      * @param {String} field A field on your records
8844      * @param {String/RegExp} value Either a string that the field
8845      * should start with or a RegExp to test against the field
8846      * @param {Boolean} anyMatch True to match any part not just the beginning
8847      */
8848     filter : function(property, value, anyMatch){
8849         var fn = this.createFilterFn(property, value, anyMatch);
8850         return fn ? this.filterBy(fn) : this.clearFilter();
8851     },
8852
8853     /**
8854      * Filter by a function. The specified function will be called with each
8855      * record in this data source. If the function returns true the record is included,
8856      * otherwise it is filtered.
8857      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
8858      * @param {Object} scope (optional) The scope of the function (defaults to this)
8859      */
8860     filterBy : function(fn, scope){
8861         this.snapshot = this.snapshot || this.data;
8862         this.data = this.queryBy(fn, scope||this);
8863         this.fireEvent("datachanged", this);
8864     },
8865
8866     /**
8867      * Query the records by a specified property.
8868      * @param {String} field A field on your records
8869      * @param {String/RegExp} value Either a string that the field
8870      * should start with or a RegExp to test against the field
8871      * @param {Boolean} anyMatch True to match any part not just the beginning
8872      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
8873      */
8874     query : function(property, value, anyMatch){
8875         var fn = this.createFilterFn(property, value, anyMatch);
8876         return fn ? this.queryBy(fn) : this.data.clone();
8877     },
8878
8879     /**
8880      * Query by a function. The specified function will be called with each
8881      * record in this data source. If the function returns true the record is included
8882      * in the results.
8883      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
8884      * @param {Object} scope (optional) The scope of the function (defaults to this)
8885       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
8886      **/
8887     queryBy : function(fn, scope){
8888         var data = this.snapshot || this.data;
8889         return data.filterBy(fn, scope||this);
8890     },
8891
8892     /**
8893      * Collects unique values for a particular dataIndex from this store.
8894      * @param {String} dataIndex The property to collect
8895      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
8896      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
8897      * @return {Array} An array of the unique values
8898      **/
8899     collect : function(dataIndex, allowNull, bypassFilter){
8900         var d = (bypassFilter === true && this.snapshot) ?
8901                 this.snapshot.items : this.data.items;
8902         var v, sv, r = [], l = {};
8903         for(var i = 0, len = d.length; i < len; i++){
8904             v = d[i].data[dataIndex];
8905             sv = String(v);
8906             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
8907                 l[sv] = true;
8908                 r[r.length] = v;
8909             }
8910         }
8911         return r;
8912     },
8913
8914     /**
8915      * Revert to a view of the Record cache with no filtering applied.
8916      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
8917      */
8918     clearFilter : function(suppressEvent){
8919         if(this.snapshot && this.snapshot != this.data){
8920             this.data = this.snapshot;
8921             delete this.snapshot;
8922             if(suppressEvent !== true){
8923                 this.fireEvent("datachanged", this);
8924             }
8925         }
8926     },
8927
8928     // private
8929     afterEdit : function(record){
8930         if(this.modified.indexOf(record) == -1){
8931             this.modified.push(record);
8932         }
8933         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
8934     },
8935     
8936     // private
8937     afterReject : function(record){
8938         this.modified.remove(record);
8939         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
8940     },
8941
8942     // private
8943     afterCommit : function(record){
8944         this.modified.remove(record);
8945         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
8946     },
8947
8948     /**
8949      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
8950      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
8951      */
8952     commitChanges : function(){
8953         var m = this.modified.slice(0);
8954         this.modified = [];
8955         for(var i = 0, len = m.length; i < len; i++){
8956             m[i].commit();
8957         }
8958     },
8959
8960     /**
8961      * Cancel outstanding changes on all changed records.
8962      */
8963     rejectChanges : function(){
8964         var m = this.modified.slice(0);
8965         this.modified = [];
8966         for(var i = 0, len = m.length; i < len; i++){
8967             m[i].reject();
8968         }
8969     },
8970
8971     onMetaChange : function(meta, rtype, o){
8972         this.recordType = rtype;
8973         this.fields = rtype.prototype.fields;
8974         delete this.snapshot;
8975         this.sortInfo = meta.sortInfo || this.sortInfo;
8976         this.modified = [];
8977         this.fireEvent('metachange', this, this.reader.meta);
8978     },
8979     
8980     moveIndex : function(data, type)
8981     {
8982         var index = this.indexOf(data);
8983         
8984         var newIndex = index + type;
8985         
8986         this.remove(data);
8987         
8988         this.insert(newIndex, data);
8989         
8990     }
8991 });/*
8992  * Based on:
8993  * Ext JS Library 1.1.1
8994  * Copyright(c) 2006-2007, Ext JS, LLC.
8995  *
8996  * Originally Released Under LGPL - original licence link has changed is not relivant.
8997  *
8998  * Fork - LGPL
8999  * <script type="text/javascript">
9000  */
9001
9002 /**
9003  * @class Roo.data.SimpleStore
9004  * @extends Roo.data.Store
9005  * Small helper class to make creating Stores from Array data easier.
9006  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
9007  * @cfg {Array} fields An array of field definition objects, or field name strings.
9008  * @cfg {Array} data The multi-dimensional array of data
9009  * @constructor
9010  * @param {Object} config
9011  */
9012 Roo.data.SimpleStore = function(config){
9013     Roo.data.SimpleStore.superclass.constructor.call(this, {
9014         isLocal : true,
9015         reader: new Roo.data.ArrayReader({
9016                 id: config.id
9017             },
9018             Roo.data.Record.create(config.fields)
9019         ),
9020         proxy : new Roo.data.MemoryProxy(config.data)
9021     });
9022     this.load();
9023 };
9024 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
9025  * Based on:
9026  * Ext JS Library 1.1.1
9027  * Copyright(c) 2006-2007, Ext JS, LLC.
9028  *
9029  * Originally Released Under LGPL - original licence link has changed is not relivant.
9030  *
9031  * Fork - LGPL
9032  * <script type="text/javascript">
9033  */
9034
9035 /**
9036 /**
9037  * @extends Roo.data.Store
9038  * @class Roo.data.JsonStore
9039  * Small helper class to make creating Stores for JSON data easier. <br/>
9040 <pre><code>
9041 var store = new Roo.data.JsonStore({
9042     url: 'get-images.php',
9043     root: 'images',
9044     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
9045 });
9046 </code></pre>
9047  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
9048  * JsonReader and HttpProxy (unless inline data is provided).</b>
9049  * @cfg {Array} fields An array of field definition objects, or field name strings.
9050  * @constructor
9051  * @param {Object} config
9052  */
9053 Roo.data.JsonStore = function(c){
9054     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
9055         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
9056         reader: new Roo.data.JsonReader(c, c.fields)
9057     }));
9058 };
9059 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
9060  * Based on:
9061  * Ext JS Library 1.1.1
9062  * Copyright(c) 2006-2007, Ext JS, LLC.
9063  *
9064  * Originally Released Under LGPL - original licence link has changed is not relivant.
9065  *
9066  * Fork - LGPL
9067  * <script type="text/javascript">
9068  */
9069
9070  
9071 Roo.data.Field = function(config){
9072     if(typeof config == "string"){
9073         config = {name: config};
9074     }
9075     Roo.apply(this, config);
9076     
9077     if(!this.type){
9078         this.type = "auto";
9079     }
9080     
9081     var st = Roo.data.SortTypes;
9082     // named sortTypes are supported, here we look them up
9083     if(typeof this.sortType == "string"){
9084         this.sortType = st[this.sortType];
9085     }
9086     
9087     // set default sortType for strings and dates
9088     if(!this.sortType){
9089         switch(this.type){
9090             case "string":
9091                 this.sortType = st.asUCString;
9092                 break;
9093             case "date":
9094                 this.sortType = st.asDate;
9095                 break;
9096             default:
9097                 this.sortType = st.none;
9098         }
9099     }
9100
9101     // define once
9102     var stripRe = /[\$,%]/g;
9103
9104     // prebuilt conversion function for this field, instead of
9105     // switching every time we're reading a value
9106     if(!this.convert){
9107         var cv, dateFormat = this.dateFormat;
9108         switch(this.type){
9109             case "":
9110             case "auto":
9111             case undefined:
9112                 cv = function(v){ return v; };
9113                 break;
9114             case "string":
9115                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
9116                 break;
9117             case "int":
9118                 cv = function(v){
9119                     return v !== undefined && v !== null && v !== '' ?
9120                            parseInt(String(v).replace(stripRe, ""), 10) : '';
9121                     };
9122                 break;
9123             case "float":
9124                 cv = function(v){
9125                     return v !== undefined && v !== null && v !== '' ?
9126                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
9127                     };
9128                 break;
9129             case "bool":
9130             case "boolean":
9131                 cv = function(v){ return v === true || v === "true" || v == 1; };
9132                 break;
9133             case "date":
9134                 cv = function(v){
9135                     if(!v){
9136                         return '';
9137                     }
9138                     if(v instanceof Date){
9139                         return v;
9140                     }
9141                     if(dateFormat){
9142                         if(dateFormat == "timestamp"){
9143                             return new Date(v*1000);
9144                         }
9145                         return Date.parseDate(v, dateFormat);
9146                     }
9147                     var parsed = Date.parse(v);
9148                     return parsed ? new Date(parsed) : null;
9149                 };
9150              break;
9151             
9152         }
9153         this.convert = cv;
9154     }
9155 };
9156
9157 Roo.data.Field.prototype = {
9158     dateFormat: null,
9159     defaultValue: "",
9160     mapping: null,
9161     sortType : null,
9162     sortDir : "ASC"
9163 };/*
9164  * Based on:
9165  * Ext JS Library 1.1.1
9166  * Copyright(c) 2006-2007, Ext JS, LLC.
9167  *
9168  * Originally Released Under LGPL - original licence link has changed is not relivant.
9169  *
9170  * Fork - LGPL
9171  * <script type="text/javascript">
9172  */
9173  
9174 // Base class for reading structured data from a data source.  This class is intended to be
9175 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
9176
9177 /**
9178  * @class Roo.data.DataReader
9179  * Base class for reading structured data from a data source.  This class is intended to be
9180  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
9181  */
9182
9183 Roo.data.DataReader = function(meta, recordType){
9184     
9185     this.meta = meta;
9186     
9187     this.recordType = recordType instanceof Array ? 
9188         Roo.data.Record.create(recordType) : recordType;
9189 };
9190
9191 Roo.data.DataReader.prototype = {
9192      /**
9193      * Create an empty record
9194      * @param {Object} data (optional) - overlay some values
9195      * @return {Roo.data.Record} record created.
9196      */
9197     newRow :  function(d) {
9198         var da =  {};
9199         this.recordType.prototype.fields.each(function(c) {
9200             switch( c.type) {
9201                 case 'int' : da[c.name] = 0; break;
9202                 case 'date' : da[c.name] = new Date(); break;
9203                 case 'float' : da[c.name] = 0.0; break;
9204                 case 'boolean' : da[c.name] = false; break;
9205                 default : da[c.name] = ""; break;
9206             }
9207             
9208         });
9209         return new this.recordType(Roo.apply(da, d));
9210     }
9211     
9212 };/*
9213  * Based on:
9214  * Ext JS Library 1.1.1
9215  * Copyright(c) 2006-2007, Ext JS, LLC.
9216  *
9217  * Originally Released Under LGPL - original licence link has changed is not relivant.
9218  *
9219  * Fork - LGPL
9220  * <script type="text/javascript">
9221  */
9222
9223 /**
9224  * @class Roo.data.DataProxy
9225  * @extends Roo.data.Observable
9226  * This class is an abstract base class for implementations which provide retrieval of
9227  * unformatted data objects.<br>
9228  * <p>
9229  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
9230  * (of the appropriate type which knows how to parse the data object) to provide a block of
9231  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
9232  * <p>
9233  * Custom implementations must implement the load method as described in
9234  * {@link Roo.data.HttpProxy#load}.
9235  */
9236 Roo.data.DataProxy = function(){
9237     this.addEvents({
9238         /**
9239          * @event beforeload
9240          * Fires before a network request is made to retrieve a data object.
9241          * @param {Object} This DataProxy object.
9242          * @param {Object} params The params parameter to the load function.
9243          */
9244         beforeload : true,
9245         /**
9246          * @event load
9247          * Fires before the load method's callback is called.
9248          * @param {Object} This DataProxy object.
9249          * @param {Object} o The data object.
9250          * @param {Object} arg The callback argument object passed to the load function.
9251          */
9252         load : true,
9253         /**
9254          * @event loadexception
9255          * Fires if an Exception occurs during data retrieval.
9256          * @param {Object} This DataProxy object.
9257          * @param {Object} o The data object.
9258          * @param {Object} arg The callback argument object passed to the load function.
9259          * @param {Object} e The Exception.
9260          */
9261         loadexception : true
9262     });
9263     Roo.data.DataProxy.superclass.constructor.call(this);
9264 };
9265
9266 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
9267
9268     /**
9269      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
9270      */
9271 /*
9272  * Based on:
9273  * Ext JS Library 1.1.1
9274  * Copyright(c) 2006-2007, Ext JS, LLC.
9275  *
9276  * Originally Released Under LGPL - original licence link has changed is not relivant.
9277  *
9278  * Fork - LGPL
9279  * <script type="text/javascript">
9280  */
9281 /**
9282  * @class Roo.data.MemoryProxy
9283  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
9284  * to the Reader when its load method is called.
9285  * @constructor
9286  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
9287  */
9288 Roo.data.MemoryProxy = function(data){
9289     if (data.data) {
9290         data = data.data;
9291     }
9292     Roo.data.MemoryProxy.superclass.constructor.call(this);
9293     this.data = data;
9294 };
9295
9296 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
9297     /**
9298      * Load data from the requested source (in this case an in-memory
9299      * data object passed to the constructor), read the data object into
9300      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9301      * process that block using the passed callback.
9302      * @param {Object} params This parameter is not used by the MemoryProxy class.
9303      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9304      * object into a block of Roo.data.Records.
9305      * @param {Function} callback The function into which to pass the block of Roo.data.records.
9306      * The function must be passed <ul>
9307      * <li>The Record block object</li>
9308      * <li>The "arg" argument from the load function</li>
9309      * <li>A boolean success indicator</li>
9310      * </ul>
9311      * @param {Object} scope The scope in which to call the callback
9312      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9313      */
9314     load : function(params, reader, callback, scope, arg){
9315         params = params || {};
9316         var result;
9317         try {
9318             result = reader.readRecords(this.data);
9319         }catch(e){
9320             this.fireEvent("loadexception", this, arg, null, e);
9321             callback.call(scope, null, arg, false);
9322             return;
9323         }
9324         callback.call(scope, result, arg, true);
9325     },
9326     
9327     // private
9328     update : function(params, records){
9329         
9330     }
9331 });/*
9332  * Based on:
9333  * Ext JS Library 1.1.1
9334  * Copyright(c) 2006-2007, Ext JS, LLC.
9335  *
9336  * Originally Released Under LGPL - original licence link has changed is not relivant.
9337  *
9338  * Fork - LGPL
9339  * <script type="text/javascript">
9340  */
9341 /**
9342  * @class Roo.data.HttpProxy
9343  * @extends Roo.data.DataProxy
9344  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
9345  * configured to reference a certain URL.<br><br>
9346  * <p>
9347  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
9348  * from which the running page was served.<br><br>
9349  * <p>
9350  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
9351  * <p>
9352  * Be aware that to enable the browser to parse an XML document, the server must set
9353  * the Content-Type header in the HTTP response to "text/xml".
9354  * @constructor
9355  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
9356  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
9357  * will be used to make the request.
9358  */
9359 Roo.data.HttpProxy = function(conn){
9360     Roo.data.HttpProxy.superclass.constructor.call(this);
9361     // is conn a conn config or a real conn?
9362     this.conn = conn;
9363     this.useAjax = !conn || !conn.events;
9364   
9365 };
9366
9367 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
9368     // thse are take from connection...
9369     
9370     /**
9371      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
9372      */
9373     /**
9374      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
9375      * extra parameters to each request made by this object. (defaults to undefined)
9376      */
9377     /**
9378      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
9379      *  to each request made by this object. (defaults to undefined)
9380      */
9381     /**
9382      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
9383      */
9384     /**
9385      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
9386      */
9387      /**
9388      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
9389      * @type Boolean
9390      */
9391   
9392
9393     /**
9394      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
9395      * @type Boolean
9396      */
9397     /**
9398      * Return the {@link Roo.data.Connection} object being used by this Proxy.
9399      * @return {Connection} The Connection object. This object may be used to subscribe to events on
9400      * a finer-grained basis than the DataProxy events.
9401      */
9402     getConnection : function(){
9403         return this.useAjax ? Roo.Ajax : this.conn;
9404     },
9405
9406     /**
9407      * Load data from the configured {@link Roo.data.Connection}, read the data object into
9408      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
9409      * process that block using the passed callback.
9410      * @param {Object} params An object containing properties which are to be used as HTTP parameters
9411      * for the request to the remote server.
9412      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9413      * object into a block of Roo.data.Records.
9414      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9415      * The function must be passed <ul>
9416      * <li>The Record block object</li>
9417      * <li>The "arg" argument from the load function</li>
9418      * <li>A boolean success indicator</li>
9419      * </ul>
9420      * @param {Object} scope The scope in which to call the callback
9421      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9422      */
9423     load : function(params, reader, callback, scope, arg){
9424         if(this.fireEvent("beforeload", this, params) !== false){
9425             var  o = {
9426                 params : params || {},
9427                 request: {
9428                     callback : callback,
9429                     scope : scope,
9430                     arg : arg
9431                 },
9432                 reader: reader,
9433                 callback : this.loadResponse,
9434                 scope: this
9435             };
9436             if(this.useAjax){
9437                 Roo.applyIf(o, this.conn);
9438                 if(this.activeRequest){
9439                     Roo.Ajax.abort(this.activeRequest);
9440                 }
9441                 this.activeRequest = Roo.Ajax.request(o);
9442             }else{
9443                 this.conn.request(o);
9444             }
9445         }else{
9446             callback.call(scope||this, null, arg, false);
9447         }
9448     },
9449
9450     // private
9451     loadResponse : function(o, success, response){
9452         delete this.activeRequest;
9453         if(!success){
9454             this.fireEvent("loadexception", this, o, response);
9455             o.request.callback.call(o.request.scope, null, o.request.arg, false);
9456             return;
9457         }
9458         var result;
9459         try {
9460             result = o.reader.read(response);
9461         }catch(e){
9462             this.fireEvent("loadexception", this, o, response, e);
9463             o.request.callback.call(o.request.scope, null, o.request.arg, false);
9464             return;
9465         }
9466         
9467         this.fireEvent("load", this, o, o.request.arg);
9468         o.request.callback.call(o.request.scope, result, o.request.arg, true);
9469     },
9470
9471     // private
9472     update : function(dataSet){
9473
9474     },
9475
9476     // private
9477     updateResponse : function(dataSet){
9478
9479     }
9480 });/*
9481  * Based on:
9482  * Ext JS Library 1.1.1
9483  * Copyright(c) 2006-2007, Ext JS, LLC.
9484  *
9485  * Originally Released Under LGPL - original licence link has changed is not relivant.
9486  *
9487  * Fork - LGPL
9488  * <script type="text/javascript">
9489  */
9490
9491 /**
9492  * @class Roo.data.ScriptTagProxy
9493  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
9494  * other than the originating domain of the running page.<br><br>
9495  * <p>
9496  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
9497  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
9498  * <p>
9499  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
9500  * source code that is used as the source inside a &lt;script> tag.<br><br>
9501  * <p>
9502  * In order for the browser to process the returned data, the server must wrap the data object
9503  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
9504  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
9505  * depending on whether the callback name was passed:
9506  * <p>
9507  * <pre><code>
9508 boolean scriptTag = false;
9509 String cb = request.getParameter("callback");
9510 if (cb != null) {
9511     scriptTag = true;
9512     response.setContentType("text/javascript");
9513 } else {
9514     response.setContentType("application/x-json");
9515 }
9516 Writer out = response.getWriter();
9517 if (scriptTag) {
9518     out.write(cb + "(");
9519 }
9520 out.print(dataBlock.toJsonString());
9521 if (scriptTag) {
9522     out.write(");");
9523 }
9524 </pre></code>
9525  *
9526  * @constructor
9527  * @param {Object} config A configuration object.
9528  */
9529 Roo.data.ScriptTagProxy = function(config){
9530     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
9531     Roo.apply(this, config);
9532     this.head = document.getElementsByTagName("head")[0];
9533 };
9534
9535 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
9536
9537 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
9538     /**
9539      * @cfg {String} url The URL from which to request the data object.
9540      */
9541     /**
9542      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
9543      */
9544     timeout : 30000,
9545     /**
9546      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
9547      * the server the name of the callback function set up by the load call to process the returned data object.
9548      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
9549      * javascript output which calls this named function passing the data object as its only parameter.
9550      */
9551     callbackParam : "callback",
9552     /**
9553      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
9554      * name to the request.
9555      */
9556     nocache : true,
9557
9558     /**
9559      * Load data from the configured URL, read the data object into
9560      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9561      * process that block using the passed callback.
9562      * @param {Object} params An object containing properties which are to be used as HTTP parameters
9563      * for the request to the remote server.
9564      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9565      * object into a block of Roo.data.Records.
9566      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9567      * The function must be passed <ul>
9568      * <li>The Record block object</li>
9569      * <li>The "arg" argument from the load function</li>
9570      * <li>A boolean success indicator</li>
9571      * </ul>
9572      * @param {Object} scope The scope in which to call the callback
9573      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9574      */
9575     load : function(params, reader, callback, scope, arg){
9576         if(this.fireEvent("beforeload", this, params) !== false){
9577
9578             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
9579
9580             var url = this.url;
9581             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
9582             if(this.nocache){
9583                 url += "&_dc=" + (new Date().getTime());
9584             }
9585             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
9586             var trans = {
9587                 id : transId,
9588                 cb : "stcCallback"+transId,
9589                 scriptId : "stcScript"+transId,
9590                 params : params,
9591                 arg : arg,
9592                 url : url,
9593                 callback : callback,
9594                 scope : scope,
9595                 reader : reader
9596             };
9597             var conn = this;
9598
9599             window[trans.cb] = function(o){
9600                 conn.handleResponse(o, trans);
9601             };
9602
9603             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
9604
9605             if(this.autoAbort !== false){
9606                 this.abort();
9607             }
9608
9609             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
9610
9611             var script = document.createElement("script");
9612             script.setAttribute("src", url);
9613             script.setAttribute("type", "text/javascript");
9614             script.setAttribute("id", trans.scriptId);
9615             this.head.appendChild(script);
9616
9617             this.trans = trans;
9618         }else{
9619             callback.call(scope||this, null, arg, false);
9620         }
9621     },
9622
9623     // private
9624     isLoading : function(){
9625         return this.trans ? true : false;
9626     },
9627
9628     /**
9629      * Abort the current server request.
9630      */
9631     abort : function(){
9632         if(this.isLoading()){
9633             this.destroyTrans(this.trans);
9634         }
9635     },
9636
9637     // private
9638     destroyTrans : function(trans, isLoaded){
9639         this.head.removeChild(document.getElementById(trans.scriptId));
9640         clearTimeout(trans.timeoutId);
9641         if(isLoaded){
9642             window[trans.cb] = undefined;
9643             try{
9644                 delete window[trans.cb];
9645             }catch(e){}
9646         }else{
9647             // if hasn't been loaded, wait for load to remove it to prevent script error
9648             window[trans.cb] = function(){
9649                 window[trans.cb] = undefined;
9650                 try{
9651                     delete window[trans.cb];
9652                 }catch(e){}
9653             };
9654         }
9655     },
9656
9657     // private
9658     handleResponse : function(o, trans){
9659         this.trans = false;
9660         this.destroyTrans(trans, true);
9661         var result;
9662         try {
9663             result = trans.reader.readRecords(o);
9664         }catch(e){
9665             this.fireEvent("loadexception", this, o, trans.arg, e);
9666             trans.callback.call(trans.scope||window, null, trans.arg, false);
9667             return;
9668         }
9669         this.fireEvent("load", this, o, trans.arg);
9670         trans.callback.call(trans.scope||window, result, trans.arg, true);
9671     },
9672
9673     // private
9674     handleFailure : function(trans){
9675         this.trans = false;
9676         this.destroyTrans(trans, false);
9677         this.fireEvent("loadexception", this, null, trans.arg);
9678         trans.callback.call(trans.scope||window, null, trans.arg, false);
9679     }
9680 });/*
9681  * Based on:
9682  * Ext JS Library 1.1.1
9683  * Copyright(c) 2006-2007, Ext JS, LLC.
9684  *
9685  * Originally Released Under LGPL - original licence link has changed is not relivant.
9686  *
9687  * Fork - LGPL
9688  * <script type="text/javascript">
9689  */
9690
9691 /**
9692  * @class Roo.data.JsonReader
9693  * @extends Roo.data.DataReader
9694  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
9695  * based on mappings in a provided Roo.data.Record constructor.
9696  * 
9697  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
9698  * in the reply previously. 
9699  * 
9700  * <p>
9701  * Example code:
9702  * <pre><code>
9703 var RecordDef = Roo.data.Record.create([
9704     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
9705     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
9706 ]);
9707 var myReader = new Roo.data.JsonReader({
9708     totalProperty: "results",    // The property which contains the total dataset size (optional)
9709     root: "rows",                // The property which contains an Array of row objects
9710     id: "id"                     // The property within each row object that provides an ID for the record (optional)
9711 }, RecordDef);
9712 </code></pre>
9713  * <p>
9714  * This would consume a JSON file like this:
9715  * <pre><code>
9716 { 'results': 2, 'rows': [
9717     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
9718     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
9719 }
9720 </code></pre>
9721  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
9722  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
9723  * paged from the remote server.
9724  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
9725  * @cfg {String} root name of the property which contains the Array of row objects.
9726  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
9727  * @constructor
9728  * Create a new JsonReader
9729  * @param {Object} meta Metadata configuration options
9730  * @param {Object} recordType Either an Array of field definition objects,
9731  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
9732  */
9733 Roo.data.JsonReader = function(meta, recordType){
9734     
9735     meta = meta || {};
9736     // set some defaults:
9737     Roo.applyIf(meta, {
9738         totalProperty: 'total',
9739         successProperty : 'success',
9740         root : 'data',
9741         id : 'id'
9742     });
9743     
9744     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
9745 };
9746 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
9747     
9748     /**
9749      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
9750      * Used by Store query builder to append _requestMeta to params.
9751      * 
9752      */
9753     metaFromRemote : false,
9754     /**
9755      * This method is only used by a DataProxy which has retrieved data from a remote server.
9756      * @param {Object} response The XHR object which contains the JSON data in its responseText.
9757      * @return {Object} data A data block which is used by an Roo.data.Store object as
9758      * a cache of Roo.data.Records.
9759      */
9760     read : function(response){
9761         var json = response.responseText;
9762        
9763         var o = /* eval:var:o */ eval("("+json+")");
9764         if(!o) {
9765             throw {message: "JsonReader.read: Json object not found"};
9766         }
9767         
9768         if(o.metaData){
9769             
9770             delete this.ef;
9771             this.metaFromRemote = true;
9772             this.meta = o.metaData;
9773             this.recordType = Roo.data.Record.create(o.metaData.fields);
9774             this.onMetaChange(this.meta, this.recordType, o);
9775         }
9776         return this.readRecords(o);
9777     },
9778
9779     // private function a store will implement
9780     onMetaChange : function(meta, recordType, o){
9781
9782     },
9783
9784     /**
9785          * @ignore
9786          */
9787     simpleAccess: function(obj, subsc) {
9788         return obj[subsc];
9789     },
9790
9791         /**
9792          * @ignore
9793          */
9794     getJsonAccessor: function(){
9795         var re = /[\[\.]/;
9796         return function(expr) {
9797             try {
9798                 return(re.test(expr))
9799                     ? new Function("obj", "return obj." + expr)
9800                     : function(obj){
9801                         return obj[expr];
9802                     };
9803             } catch(e){}
9804             return Roo.emptyFn;
9805         };
9806     }(),
9807
9808     /**
9809      * Create a data block containing Roo.data.Records from an XML document.
9810      * @param {Object} o An object which contains an Array of row objects in the property specified
9811      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
9812      * which contains the total size of the dataset.
9813      * @return {Object} data A data block which is used by an Roo.data.Store object as
9814      * a cache of Roo.data.Records.
9815      */
9816     readRecords : function(o){
9817         /**
9818          * After any data loads, the raw JSON data is available for further custom processing.
9819          * @type Object
9820          */
9821         this.o = o;
9822         var s = this.meta, Record = this.recordType,
9823             f = Record.prototype.fields, fi = f.items, fl = f.length;
9824
9825 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
9826         if (!this.ef) {
9827             if(s.totalProperty) {
9828                     this.getTotal = this.getJsonAccessor(s.totalProperty);
9829                 }
9830                 if(s.successProperty) {
9831                     this.getSuccess = this.getJsonAccessor(s.successProperty);
9832                 }
9833                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
9834                 if (s.id) {
9835                         var g = this.getJsonAccessor(s.id);
9836                         this.getId = function(rec) {
9837                                 var r = g(rec);
9838                                 return (r === undefined || r === "") ? null : r;
9839                         };
9840                 } else {
9841                         this.getId = function(){return null;};
9842                 }
9843             this.ef = [];
9844             for(var jj = 0; jj < fl; jj++){
9845                 f = fi[jj];
9846                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
9847                 this.ef[jj] = this.getJsonAccessor(map);
9848             }
9849         }
9850
9851         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
9852         if(s.totalProperty){
9853             var vt = parseInt(this.getTotal(o), 10);
9854             if(!isNaN(vt)){
9855                 totalRecords = vt;
9856             }
9857         }
9858         if(s.successProperty){
9859             var vs = this.getSuccess(o);
9860             if(vs === false || vs === 'false'){
9861                 success = false;
9862             }
9863         }
9864         var records = [];
9865             for(var i = 0; i < c; i++){
9866                     var n = root[i];
9867                 var values = {};
9868                 var id = this.getId(n);
9869                 for(var j = 0; j < fl; j++){
9870                     f = fi[j];
9871                 var v = this.ef[j](n);
9872                 if (!f.convert) {
9873                     Roo.log('missing convert for ' + f.name);
9874                     Roo.log(f);
9875                     continue;
9876                 }
9877                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
9878                 }
9879                 var record = new Record(values, id);
9880                 record.json = n;
9881                 records[i] = record;
9882             }
9883             return {
9884             raw : o,
9885                 success : success,
9886                 records : records,
9887                 totalRecords : totalRecords
9888             };
9889     }
9890 });/*
9891  * Based on:
9892  * Ext JS Library 1.1.1
9893  * Copyright(c) 2006-2007, Ext JS, LLC.
9894  *
9895  * Originally Released Under LGPL - original licence link has changed is not relivant.
9896  *
9897  * Fork - LGPL
9898  * <script type="text/javascript">
9899  */
9900
9901 /**
9902  * @class Roo.data.ArrayReader
9903  * @extends Roo.data.DataReader
9904  * Data reader class to create an Array of Roo.data.Record objects from an Array.
9905  * Each element of that Array represents a row of data fields. The
9906  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
9907  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
9908  * <p>
9909  * Example code:.
9910  * <pre><code>
9911 var RecordDef = Roo.data.Record.create([
9912     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
9913     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
9914 ]);
9915 var myReader = new Roo.data.ArrayReader({
9916     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
9917 }, RecordDef);
9918 </code></pre>
9919  * <p>
9920  * This would consume an Array like this:
9921  * <pre><code>
9922 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
9923   </code></pre>
9924  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
9925  * @constructor
9926  * Create a new JsonReader
9927  * @param {Object} meta Metadata configuration options.
9928  * @param {Object} recordType Either an Array of field definition objects
9929  * as specified to {@link Roo.data.Record#create},
9930  * or an {@link Roo.data.Record} object
9931  * created using {@link Roo.data.Record#create}.
9932  */
9933 Roo.data.ArrayReader = function(meta, recordType){
9934     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
9935 };
9936
9937 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
9938     /**
9939      * Create a data block containing Roo.data.Records from an XML document.
9940      * @param {Object} o An Array of row objects which represents the dataset.
9941      * @return {Object} data A data block which is used by an Roo.data.Store object as
9942      * a cache of Roo.data.Records.
9943      */
9944     readRecords : function(o){
9945         var sid = this.meta ? this.meta.id : null;
9946         var recordType = this.recordType, fields = recordType.prototype.fields;
9947         var records = [];
9948         var root = o;
9949             for(var i = 0; i < root.length; i++){
9950                     var n = root[i];
9951                 var values = {};
9952                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
9953                 for(var j = 0, jlen = fields.length; j < jlen; j++){
9954                 var f = fields.items[j];
9955                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
9956                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
9957                 v = f.convert(v);
9958                 values[f.name] = v;
9959             }
9960                 var record = new recordType(values, id);
9961                 record.json = n;
9962                 records[records.length] = record;
9963             }
9964             return {
9965                 records : records,
9966                 totalRecords : records.length
9967             };
9968     }
9969 });/*
9970  * - LGPL
9971  * * 
9972  */
9973
9974 /**
9975  * @class Roo.bootstrap.ComboBox
9976  * @extends Roo.bootstrap.TriggerField
9977  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
9978  * @cfg {Boolean} append (true|false) default false
9979  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
9980  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
9981  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
9982  * @constructor
9983  * Create a new ComboBox.
9984  * @param {Object} config Configuration options
9985  */
9986 Roo.bootstrap.ComboBox = function(config){
9987     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
9988     this.addEvents({
9989         /**
9990          * @event expand
9991          * Fires when the dropdown list is expanded
9992              * @param {Roo.bootstrap.ComboBox} combo This combo box
9993              */
9994         'expand' : true,
9995         /**
9996          * @event collapse
9997          * Fires when the dropdown list is collapsed
9998              * @param {Roo.bootstrap.ComboBox} combo This combo box
9999              */
10000         'collapse' : true,
10001         /**
10002          * @event beforeselect
10003          * Fires before a list item is selected. Return false to cancel the selection.
10004              * @param {Roo.bootstrap.ComboBox} combo This combo box
10005              * @param {Roo.data.Record} record The data record returned from the underlying store
10006              * @param {Number} index The index of the selected item in the dropdown list
10007              */
10008         'beforeselect' : true,
10009         /**
10010          * @event select
10011          * Fires when a list item is selected
10012              * @param {Roo.bootstrap.ComboBox} combo This combo box
10013              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
10014              * @param {Number} index The index of the selected item in the dropdown list
10015              */
10016         'select' : true,
10017         /**
10018          * @event beforequery
10019          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
10020          * The event object passed has these properties:
10021              * @param {Roo.bootstrap.ComboBox} combo This combo box
10022              * @param {String} query The query
10023              * @param {Boolean} forceAll true to force "all" query
10024              * @param {Boolean} cancel true to cancel the query
10025              * @param {Object} e The query event object
10026              */
10027         'beforequery': true,
10028          /**
10029          * @event add
10030          * Fires when the 'add' icon is pressed (add a listener to enable add button)
10031              * @param {Roo.bootstrap.ComboBox} combo This combo box
10032              */
10033         'add' : true,
10034         /**
10035          * @event edit
10036          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
10037              * @param {Roo.bootstrap.ComboBox} combo This combo box
10038              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
10039              */
10040         'edit' : true,
10041         /**
10042          * @event remove
10043          * Fires when the remove value from the combobox array
10044              * @param {Roo.bootstrap.ComboBox} combo This combo box
10045              */
10046         'remove' : true
10047         
10048     });
10049     
10050     this.item = [];
10051     this.tickItems = [];
10052     
10053     this.selectedIndex = -1;
10054     if(this.mode == 'local'){
10055         if(config.queryDelay === undefined){
10056             this.queryDelay = 10;
10057         }
10058         if(config.minChars === undefined){
10059             this.minChars = 0;
10060         }
10061     }
10062 };
10063
10064 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
10065      
10066     /**
10067      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
10068      * rendering into an Roo.Editor, defaults to false)
10069      */
10070     /**
10071      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
10072      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
10073      */
10074     /**
10075      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
10076      */
10077     /**
10078      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
10079      * the dropdown list (defaults to undefined, with no header element)
10080      */
10081
10082      /**
10083      * @cfg {String/Roo.Template} tpl The template to use to render the output
10084      */
10085      
10086      /**
10087      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
10088      */
10089     listWidth: undefined,
10090     /**
10091      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
10092      * mode = 'remote' or 'text' if mode = 'local')
10093      */
10094     displayField: undefined,
10095     /**
10096      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
10097      * mode = 'remote' or 'value' if mode = 'local'). 
10098      * Note: use of a valueField requires the user make a selection
10099      * in order for a value to be mapped.
10100      */
10101     valueField: undefined,
10102     
10103     
10104     /**
10105      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
10106      * field's data value (defaults to the underlying DOM element's name)
10107      */
10108     hiddenName: undefined,
10109     /**
10110      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
10111      */
10112     listClass: '',
10113     /**
10114      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
10115      */
10116     selectedClass: 'active',
10117     
10118     /**
10119      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
10120      */
10121     shadow:'sides',
10122     /**
10123      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
10124      * anchor positions (defaults to 'tl-bl')
10125      */
10126     listAlign: 'tl-bl?',
10127     /**
10128      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
10129      */
10130     maxHeight: 300,
10131     /**
10132      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
10133      * query specified by the allQuery config option (defaults to 'query')
10134      */
10135     triggerAction: 'query',
10136     /**
10137      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
10138      * (defaults to 4, does not apply if editable = false)
10139      */
10140     minChars : 4,
10141     /**
10142      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
10143      * delay (typeAheadDelay) if it matches a known value (defaults to false)
10144      */
10145     typeAhead: false,
10146     /**
10147      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
10148      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
10149      */
10150     queryDelay: 500,
10151     /**
10152      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
10153      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
10154      */
10155     pageSize: 0,
10156     /**
10157      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
10158      * when editable = true (defaults to false)
10159      */
10160     selectOnFocus:false,
10161     /**
10162      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
10163      */
10164     queryParam: 'query',
10165     /**
10166      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
10167      * when mode = 'remote' (defaults to 'Loading...')
10168      */
10169     loadingText: 'Loading...',
10170     /**
10171      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
10172      */
10173     resizable: false,
10174     /**
10175      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
10176      */
10177     handleHeight : 8,
10178     /**
10179      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
10180      * traditional select (defaults to true)
10181      */
10182     editable: true,
10183     /**
10184      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
10185      */
10186     allQuery: '',
10187     /**
10188      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
10189      */
10190     mode: 'remote',
10191     /**
10192      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
10193      * listWidth has a higher value)
10194      */
10195     minListWidth : 70,
10196     /**
10197      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
10198      * allow the user to set arbitrary text into the field (defaults to false)
10199      */
10200     forceSelection:false,
10201     /**
10202      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
10203      * if typeAhead = true (defaults to 250)
10204      */
10205     typeAheadDelay : 250,
10206     /**
10207      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
10208      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
10209      */
10210     valueNotFoundText : undefined,
10211     /**
10212      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
10213      */
10214     blockFocus : false,
10215     
10216     /**
10217      * @cfg {Boolean} disableClear Disable showing of clear button.
10218      */
10219     disableClear : false,
10220     /**
10221      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
10222      */
10223     alwaysQuery : false,
10224     
10225     /**
10226      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
10227      */
10228     multiple : false,
10229     
10230     //private
10231     addicon : false,
10232     editicon: false,
10233     
10234     page: 0,
10235     hasQuery: false,
10236     append: false,
10237     loadNext: false,
10238     autoFocus : true,
10239     tickable : false,
10240     btnPosition : 'right',
10241     
10242     // element that contains real text value.. (when hidden is used..)
10243     
10244     getAutoCreate : function()
10245     {
10246         var cfg = false;
10247         
10248         /*
10249          *  Normal ComboBox
10250          */
10251         if(!this.tickable){
10252             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
10253             return cfg;
10254         }
10255         
10256         /*
10257          *  ComboBox with tickable selections
10258          */
10259              
10260         var align = this.labelAlign || this.parentLabelAlign();
10261         
10262         cfg = {
10263             cls : 'form-group roo-combobox-tickable' //input-group
10264         };
10265         
10266         
10267         var buttons = {
10268             tag : 'div',
10269             cls : 'tickable-buttons',
10270             cn : [
10271                 {
10272                     tag : 'button',
10273                     type : 'button',
10274                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
10275                     html : 'Edit'
10276                 },
10277                 {
10278                     tag : 'button',
10279                     type : 'button',
10280                     name : 'ok',
10281                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
10282                     html : 'Done'
10283                 },
10284                 {
10285                     tag : 'button',
10286                     type : 'button',
10287                     name : 'cancel',
10288                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
10289                     html : 'Cancel'
10290                 }
10291             ]
10292         };
10293         
10294         var _this = this;
10295         Roo.each(buttons.cn, function(c){
10296             if (_this.size) {
10297                 c.cls += ' btn-' + _this.size;
10298             }
10299
10300             if (_this.disabled) {
10301                 c.disabled = true;
10302             }
10303         });
10304         
10305         var box = {
10306             tag: 'div',
10307             cn: [
10308                 {
10309                     tag: 'input',
10310                     type : 'hidden',
10311                     cls: 'form-hidden-field'
10312                 },
10313                 {
10314                     tag: 'ul',
10315                     cls: 'select2-choices',
10316                     cn:[
10317                         {
10318                             tag: 'li',
10319                             cls: 'select2-search-field',
10320                             cn: [
10321
10322                                 buttons
10323                             ]
10324                         }
10325                     ]
10326                 }
10327             ]
10328         }
10329         
10330         var combobox = {
10331             cls: 'select2-container input-group select2-container-multi',
10332             cn: [
10333                 box,
10334                 {
10335                     tag: 'ul',
10336                     cls: 'typeahead typeahead-long dropdown-menu',
10337                     style: 'display:none; max-height:' + this.maxHeight + 'px;'
10338                 }
10339             ]
10340         };
10341         
10342         if (align ==='left' && this.fieldLabel.length) {
10343             
10344                 Roo.log("left and has label");
10345                 cfg.cn = [
10346                     
10347                     {
10348                         tag: 'label',
10349                         'for' :  id,
10350                         cls : 'control-label col-sm-' + this.labelWidth,
10351                         html : this.fieldLabel
10352                         
10353                     },
10354                     {
10355                         cls : "col-sm-" + (12 - this.labelWidth), 
10356                         cn: [
10357                             combobox
10358                         ]
10359                     }
10360                     
10361                 ];
10362         } else if ( this.fieldLabel.length) {
10363                 Roo.log(" label");
10364                  cfg.cn = [
10365                    
10366                     {
10367                         tag: 'label',
10368                         //cls : 'input-group-addon',
10369                         html : this.fieldLabel
10370                         
10371                     },
10372                     
10373                     combobox
10374                     
10375                 ];
10376
10377         } else {
10378             
10379                 Roo.log(" no label && no align");
10380                 cfg = combobox
10381                      
10382                 
10383         }
10384          
10385         var settings=this;
10386         ['xs','sm','md','lg'].map(function(size){
10387             if (settings[size]) {
10388                 cfg.cls += ' col-' + size + '-' + settings[size];
10389             }
10390         });
10391         
10392         return cfg;
10393         
10394     },
10395     
10396     // private
10397     initEvents: function()
10398     {
10399         
10400         if (!this.store) {
10401             throw "can not find store for combo";
10402         }
10403         this.store = Roo.factory(this.store, Roo.data);
10404         
10405         if(this.tickable){
10406             this.initTickableEvnets();
10407             return;
10408         }
10409         
10410         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
10411         
10412         
10413         if(this.hiddenName){
10414             
10415             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10416             
10417             this.hiddenField.dom.value =
10418                 this.hiddenValue !== undefined ? this.hiddenValue :
10419                 this.value !== undefined ? this.value : '';
10420
10421             // prevent input submission
10422             this.el.dom.removeAttribute('name');
10423             this.hiddenField.dom.setAttribute('name', this.hiddenName);
10424              
10425              
10426         }
10427         //if(Roo.isGecko){
10428         //    this.el.dom.setAttribute('autocomplete', 'off');
10429         //}
10430
10431         var cls = 'x-combo-list';
10432         this.list = this.el.select('ul.dropdown-menu',true).first();
10433
10434         //this.list = new Roo.Layer({
10435         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
10436         //});
10437         
10438         var _this = this;
10439         
10440         (function(){
10441             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
10442             _this.list.setWidth(lw);
10443         }).defer(100);
10444         
10445         this.list.on('mouseover', this.onViewOver, this);
10446         this.list.on('mousemove', this.onViewMove, this);
10447         
10448         this.list.on('scroll', this.onViewScroll, this);
10449         
10450         /*
10451         this.list.swallowEvent('mousewheel');
10452         this.assetHeight = 0;
10453
10454         if(this.title){
10455             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
10456             this.assetHeight += this.header.getHeight();
10457         }
10458
10459         this.innerList = this.list.createChild({cls:cls+'-inner'});
10460         this.innerList.on('mouseover', this.onViewOver, this);
10461         this.innerList.on('mousemove', this.onViewMove, this);
10462         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
10463         
10464         if(this.allowBlank && !this.pageSize && !this.disableClear){
10465             this.footer = this.list.createChild({cls:cls+'-ft'});
10466             this.pageTb = new Roo.Toolbar(this.footer);
10467            
10468         }
10469         if(this.pageSize){
10470             this.footer = this.list.createChild({cls:cls+'-ft'});
10471             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
10472                     {pageSize: this.pageSize});
10473             
10474         }
10475         
10476         if (this.pageTb && this.allowBlank && !this.disableClear) {
10477             var _this = this;
10478             this.pageTb.add(new Roo.Toolbar.Fill(), {
10479                 cls: 'x-btn-icon x-btn-clear',
10480                 text: '&#160;',
10481                 handler: function()
10482                 {
10483                     _this.collapse();
10484                     _this.clearValue();
10485                     _this.onSelect(false, -1);
10486                 }
10487             });
10488         }
10489         if (this.footer) {
10490             this.assetHeight += this.footer.getHeight();
10491         }
10492         */
10493             
10494         if(!this.tpl){
10495             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
10496         }
10497
10498         this.view = new Roo.View(this.el.select('ul.dropdown-menu',true).first(), this.tpl, {
10499             singleSelect:true, store: this.store, selectedClass: this.selectedClass
10500         });
10501         //this.view.wrapEl.setDisplayed(false);
10502         this.view.on('click', this.onViewClick, this);
10503         
10504         
10505         
10506         this.store.on('beforeload', this.onBeforeLoad, this);
10507         this.store.on('load', this.onLoad, this);
10508         this.store.on('loadexception', this.onLoadException, this);
10509         /*
10510         if(this.resizable){
10511             this.resizer = new Roo.Resizable(this.list,  {
10512                pinned:true, handles:'se'
10513             });
10514             this.resizer.on('resize', function(r, w, h){
10515                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
10516                 this.listWidth = w;
10517                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
10518                 this.restrictHeight();
10519             }, this);
10520             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
10521         }
10522         */
10523         if(!this.editable){
10524             this.editable = true;
10525             this.setEditable(false);
10526         }
10527         
10528         /*
10529         
10530         if (typeof(this.events.add.listeners) != 'undefined') {
10531             
10532             this.addicon = this.wrap.createChild(
10533                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
10534        
10535             this.addicon.on('click', function(e) {
10536                 this.fireEvent('add', this);
10537             }, this);
10538         }
10539         if (typeof(this.events.edit.listeners) != 'undefined') {
10540             
10541             this.editicon = this.wrap.createChild(
10542                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
10543             if (this.addicon) {
10544                 this.editicon.setStyle('margin-left', '40px');
10545             }
10546             this.editicon.on('click', function(e) {
10547                 
10548                 // we fire even  if inothing is selected..
10549                 this.fireEvent('edit', this, this.lastData );
10550                 
10551             }, this);
10552         }
10553         */
10554         
10555         this.keyNav = new Roo.KeyNav(this.inputEl(), {
10556             "up" : function(e){
10557                 this.inKeyMode = true;
10558                 this.selectPrev();
10559             },
10560
10561             "down" : function(e){
10562                 if(!this.isExpanded()){
10563                     this.onTriggerClick();
10564                 }else{
10565                     this.inKeyMode = true;
10566                     this.selectNext();
10567                 }
10568             },
10569
10570             "enter" : function(e){
10571 //                this.onViewClick();
10572                 //return true;
10573                 this.collapse();
10574                 
10575                 if(this.fireEvent("specialkey", this, e)){
10576                     this.onViewClick(false);
10577                 }
10578                 
10579                 return true;
10580             },
10581
10582             "esc" : function(e){
10583                 this.collapse();
10584             },
10585
10586             "tab" : function(e){
10587                 this.collapse();
10588                 
10589                 if(this.fireEvent("specialkey", this, e)){
10590                     this.onViewClick(false);
10591                 }
10592                 
10593                 return true;
10594             },
10595
10596             scope : this,
10597
10598             doRelay : function(foo, bar, hname){
10599                 if(hname == 'down' || this.scope.isExpanded()){
10600                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
10601                 }
10602                 return true;
10603             },
10604
10605             forceKeyDown: true
10606         });
10607         
10608         
10609         this.queryDelay = Math.max(this.queryDelay || 10,
10610                 this.mode == 'local' ? 10 : 250);
10611         
10612         
10613         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
10614         
10615         if(this.typeAhead){
10616             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
10617         }
10618         if(this.editable !== false){
10619             this.inputEl().on("keyup", this.onKeyUp, this);
10620         }
10621         if(this.forceSelection){
10622             this.inputEl().on('blur', this.doForce, this);
10623         }
10624         
10625         if(this.multiple){
10626             this.choices = this.el.select('ul.select2-choices', true).first();
10627             this.searchField = this.el.select('ul li.select2-search-field', true).first();
10628         }
10629     },
10630     
10631     initTickableEvnets: function()
10632     {   
10633         if(this.hiddenName){
10634             
10635             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10636             
10637             this.hiddenField.dom.value =
10638                 this.hiddenValue !== undefined ? this.hiddenValue :
10639                 this.value !== undefined ? this.value : '';
10640
10641             // prevent input submission
10642             this.el.dom.removeAttribute('name');
10643             this.hiddenField.dom.setAttribute('name', this.hiddenName);
10644              
10645              
10646         }
10647         
10648         this.list = this.el.select('ul.dropdown-menu',true).first();
10649         
10650         this.choices = this.el.select('ul.select2-choices', true).first();
10651         this.searchField = this.el.select('ul li.select2-search-field', true).first();
10652         
10653         this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
10654          
10655         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
10656         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
10657         
10658         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
10659         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
10660         
10661         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
10662         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
10663         
10664         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
10665         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
10666         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
10667         
10668         this.okBtn.hide();
10669         this.cancelBtn.hide();
10670         
10671         var _this = this;
10672         
10673         (function(){
10674             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
10675             _this.list.setWidth(lw);
10676         }).defer(100);
10677         
10678         this.list.on('mouseover', this.onViewOver, this);
10679         this.list.on('mousemove', this.onViewMove, this);
10680         
10681         this.list.on('scroll', this.onViewScroll, this);
10682         
10683         if(!this.tpl){
10684             this.tpl = '<li class="select2-result"><div class="checkbox"><input id="{roo-id}" type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></li>';
10685         }
10686
10687         this.view = new Roo.View(this.el.select('ul.dropdown-menu',true).first(), this.tpl, {
10688             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
10689         });
10690         
10691         //this.view.wrapEl.setDisplayed(false);
10692         this.view.on('click', this.onViewClick, this);
10693         
10694         
10695         
10696         this.store.on('beforeload', this.onBeforeLoad, this);
10697         this.store.on('load', this.onLoad, this);
10698         this.store.on('loadexception', this.onLoadException, this);
10699         
10700 //        this.keyNav = new Roo.KeyNav(this.inputEl(), {
10701 //            "up" : function(e){
10702 //                this.inKeyMode = true;
10703 //                this.selectPrev();
10704 //            },
10705 //
10706 //            "down" : function(e){
10707 //                if(!this.isExpanded()){
10708 //                    this.onTriggerClick();
10709 //                }else{
10710 //                    this.inKeyMode = true;
10711 //                    this.selectNext();
10712 //                }
10713 //            },
10714 //
10715 //            "enter" : function(e){
10716 ////                this.onViewClick();
10717 //                //return true;
10718 //                this.collapse();
10719 //                
10720 //                if(this.fireEvent("specialkey", this, e)){
10721 //                    this.onViewClick(false);
10722 //                }
10723 //                
10724 //                return true;
10725 //            },
10726 //
10727 //            "esc" : function(e){
10728 //                this.collapse();
10729 //            },
10730 //
10731 //            "tab" : function(e){
10732 //                this.collapse();
10733 //                
10734 //                if(this.fireEvent("specialkey", this, e)){
10735 //                    this.onViewClick(false);
10736 //                }
10737 //                
10738 //                return true;
10739 //            },
10740 //
10741 //            scope : this,
10742 //
10743 //            doRelay : function(foo, bar, hname){
10744 //                if(hname == 'down' || this.scope.isExpanded()){
10745 //                   return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
10746 //                }
10747 //                return true;
10748 //            },
10749 //
10750 //            forceKeyDown: true
10751 //        });
10752         
10753         
10754         this.queryDelay = Math.max(this.queryDelay || 10,
10755                 this.mode == 'local' ? 10 : 250);
10756         
10757         
10758         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
10759         
10760         if(this.typeAhead){
10761             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
10762         }
10763     },
10764
10765     onDestroy : function(){
10766         if(this.view){
10767             this.view.setStore(null);
10768             this.view.el.removeAllListeners();
10769             this.view.el.remove();
10770             this.view.purgeListeners();
10771         }
10772         if(this.list){
10773             this.list.dom.innerHTML  = '';
10774         }
10775         
10776         if(this.store){
10777             this.store.un('beforeload', this.onBeforeLoad, this);
10778             this.store.un('load', this.onLoad, this);
10779             this.store.un('loadexception', this.onLoadException, this);
10780         }
10781         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
10782     },
10783
10784     // private
10785     fireKey : function(e){
10786         if(e.isNavKeyPress() && !this.list.isVisible()){
10787             this.fireEvent("specialkey", this, e);
10788         }
10789     },
10790
10791     // private
10792     onResize: function(w, h){
10793 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
10794 //        
10795 //        if(typeof w != 'number'){
10796 //            // we do not handle it!?!?
10797 //            return;
10798 //        }
10799 //        var tw = this.trigger.getWidth();
10800 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
10801 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
10802 //        var x = w - tw;
10803 //        this.inputEl().setWidth( this.adjustWidth('input', x));
10804 //            
10805 //        //this.trigger.setStyle('left', x+'px');
10806 //        
10807 //        if(this.list && this.listWidth === undefined){
10808 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
10809 //            this.list.setWidth(lw);
10810 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
10811 //        }
10812         
10813     
10814         
10815     },
10816
10817     /**
10818      * Allow or prevent the user from directly editing the field text.  If false is passed,
10819      * the user will only be able to select from the items defined in the dropdown list.  This method
10820      * is the runtime equivalent of setting the 'editable' config option at config time.
10821      * @param {Boolean} value True to allow the user to directly edit the field text
10822      */
10823     setEditable : function(value){
10824         if(value == this.editable){
10825             return;
10826         }
10827         this.editable = value;
10828         if(!value){
10829             this.inputEl().dom.setAttribute('readOnly', true);
10830             this.inputEl().on('mousedown', this.onTriggerClick,  this);
10831             this.inputEl().addClass('x-combo-noedit');
10832         }else{
10833             this.inputEl().dom.setAttribute('readOnly', false);
10834             this.inputEl().un('mousedown', this.onTriggerClick,  this);
10835             this.inputEl().removeClass('x-combo-noedit');
10836         }
10837     },
10838
10839     // private
10840     
10841     onBeforeLoad : function(combo,opts){
10842         if(!this.hasFocus){
10843             return;
10844         }
10845          if (!opts.add) {
10846             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
10847          }
10848         this.restrictHeight();
10849         this.selectedIndex = -1;
10850     },
10851
10852     // private
10853     onLoad : function(){
10854         
10855         this.hasQuery = false;
10856         
10857         if(!this.hasFocus){
10858             return;
10859         }
10860         
10861         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
10862             this.loading.hide();
10863         }
10864         
10865         if(this.store.getCount() > 0){
10866             this.expand();
10867             this.restrictHeight();
10868             if(this.lastQuery == this.allQuery){
10869                 if(this.editable && !this.tickable){
10870                     this.inputEl().dom.select();
10871                 }
10872                 if(!this.selectByValue(this.value, true) && this.autoFocus){
10873                     this.select(0, true);
10874                 }
10875             }else{
10876                 if(this.autoFocus){
10877                     this.selectNext();
10878                 }
10879                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
10880                     this.taTask.delay(this.typeAheadDelay);
10881                 }
10882             }
10883         }else{
10884             this.onEmptyResults();
10885         }
10886         
10887         //this.el.focus();
10888     },
10889     // private
10890     onLoadException : function()
10891     {
10892         this.hasQuery = false;
10893         
10894         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
10895             this.loading.hide();
10896         }
10897         
10898         this.collapse();
10899         Roo.log(this.store.reader.jsonData);
10900         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
10901             // fixme
10902             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
10903         }
10904         
10905         
10906     },
10907     // private
10908     onTypeAhead : function(){
10909         if(this.store.getCount() > 0){
10910             var r = this.store.getAt(0);
10911             var newValue = r.data[this.displayField];
10912             var len = newValue.length;
10913             var selStart = this.getRawValue().length;
10914             
10915             if(selStart != len){
10916                 this.setRawValue(newValue);
10917                 this.selectText(selStart, newValue.length);
10918             }
10919         }
10920     },
10921
10922     // private
10923     onSelect : function(record, index){
10924         
10925         if(this.fireEvent('beforeselect', this, record, index) !== false){
10926         
10927             this.setFromData(index > -1 ? record.data : false);
10928             
10929             this.collapse();
10930             this.fireEvent('select', this, record, index);
10931         }
10932     },
10933
10934     /**
10935      * Returns the currently selected field value or empty string if no value is set.
10936      * @return {String} value The selected value
10937      */
10938     getValue : function(){
10939         
10940         if(this.multiple){
10941             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
10942         }
10943         
10944         if(this.valueField){
10945             return typeof this.value != 'undefined' ? this.value : '';
10946         }else{
10947             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
10948         }
10949     },
10950
10951     /**
10952      * Clears any text/value currently set in the field
10953      */
10954     clearValue : function(){
10955         if(this.hiddenField){
10956             this.hiddenField.dom.value = '';
10957         }
10958         this.value = '';
10959         this.setRawValue('');
10960         this.lastSelectionText = '';
10961         
10962     },
10963
10964     /**
10965      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
10966      * will be displayed in the field.  If the value does not match the data value of an existing item,
10967      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
10968      * Otherwise the field will be blank (although the value will still be set).
10969      * @param {String} value The value to match
10970      */
10971     setValue : function(v){
10972         if(this.multiple){
10973             this.syncValue();
10974             return;
10975         }
10976         
10977         var text = v;
10978         if(this.valueField){
10979             var r = this.findRecord(this.valueField, v);
10980             if(r){
10981                 text = r.data[this.displayField];
10982             }else if(this.valueNotFoundText !== undefined){
10983                 text = this.valueNotFoundText;
10984             }
10985         }
10986         this.lastSelectionText = text;
10987         if(this.hiddenField){
10988             this.hiddenField.dom.value = v;
10989         }
10990         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
10991         this.value = v;
10992     },
10993     /**
10994      * @property {Object} the last set data for the element
10995      */
10996     
10997     lastData : false,
10998     /**
10999      * Sets the value of the field based on a object which is related to the record format for the store.
11000      * @param {Object} value the value to set as. or false on reset?
11001      */
11002     setFromData : function(o){
11003         
11004         if(this.multiple){
11005             this.addItem(o);
11006             return;
11007         }
11008             
11009         var dv = ''; // display value
11010         var vv = ''; // value value..
11011         this.lastData = o;
11012         if (this.displayField) {
11013             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11014         } else {
11015             // this is an error condition!!!
11016             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
11017         }
11018         
11019         if(this.valueField){
11020             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
11021         }
11022         
11023         if(this.hiddenField){
11024             this.hiddenField.dom.value = vv;
11025             
11026             this.lastSelectionText = dv;
11027             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11028             this.value = vv;
11029             return;
11030         }
11031         // no hidden field.. - we store the value in 'value', but still display
11032         // display field!!!!
11033         this.lastSelectionText = dv;
11034         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11035         this.value = vv;
11036         
11037         
11038     },
11039     // private
11040     reset : function(){
11041         // overridden so that last data is reset..
11042         this.setValue(this.originalValue);
11043         this.clearInvalid();
11044         this.lastData = false;
11045         if (this.view) {
11046             this.view.clearSelections();
11047         }
11048     },
11049     // private
11050     findRecord : function(prop, value){
11051         var record;
11052         if(this.store.getCount() > 0){
11053             this.store.each(function(r){
11054                 if(r.data[prop] == value){
11055                     record = r;
11056                     return false;
11057                 }
11058                 return true;
11059             });
11060         }
11061         return record;
11062     },
11063     
11064     getName: function()
11065     {
11066         // returns hidden if it's set..
11067         if (!this.rendered) {return ''};
11068         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
11069         
11070     },
11071     // private
11072     onViewMove : function(e, t){
11073         this.inKeyMode = false;
11074     },
11075
11076     // private
11077     onViewOver : function(e, t){
11078         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
11079             return;
11080         }
11081         var item = this.view.findItemFromChild(t);
11082         
11083         if(item){
11084             var index = this.view.indexOf(item);
11085             this.select(index, false);
11086         }
11087     },
11088
11089     // private
11090     onViewClick : function(view, doFocus, el, e)
11091     {
11092         var index = this.view.getSelectedIndexes()[0];
11093         
11094         var r = this.store.getAt(index);
11095         
11096         if(this.tickable){
11097             
11098             if(e.getTarget().nodeName.toLowerCase() != 'input'){
11099                 return;
11100             }
11101             
11102             var rm = false;
11103             var _this = this;
11104             
11105             Roo.each(this.tickItems, function(v,k){
11106                 
11107                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
11108                     _this.tickItems.splice(k, 1);
11109                     rm = true;
11110                     return;
11111                 }
11112             })
11113             
11114             if(rm){
11115                 return;
11116             }
11117             
11118             this.tickItems.push(r.data);
11119             return;
11120         }
11121         
11122         if(r){
11123             this.onSelect(r, index);
11124         }
11125         if(doFocus !== false && !this.blockFocus){
11126             this.inputEl().focus();
11127         }
11128     },
11129
11130     // private
11131     restrictHeight : function(){
11132         //this.innerList.dom.style.height = '';
11133         //var inner = this.innerList.dom;
11134         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
11135         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
11136         //this.list.beginUpdate();
11137         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
11138         this.list.alignTo(this.inputEl(), this.listAlign);
11139         //this.list.endUpdate();
11140     },
11141
11142     // private
11143     onEmptyResults : function(){
11144         this.collapse();
11145     },
11146
11147     /**
11148      * Returns true if the dropdown list is expanded, else false.
11149      */
11150     isExpanded : function(){
11151         return this.list.isVisible();
11152     },
11153
11154     /**
11155      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
11156      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11157      * @param {String} value The data value of the item to select
11158      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11159      * selected item if it is not currently in view (defaults to true)
11160      * @return {Boolean} True if the value matched an item in the list, else false
11161      */
11162     selectByValue : function(v, scrollIntoView){
11163         if(v !== undefined && v !== null){
11164             var r = this.findRecord(this.valueField || this.displayField, v);
11165             if(r){
11166                 this.select(this.store.indexOf(r), scrollIntoView);
11167                 return true;
11168             }
11169         }
11170         return false;
11171     },
11172
11173     /**
11174      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
11175      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11176      * @param {Number} index The zero-based index of the list item to select
11177      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11178      * selected item if it is not currently in view (defaults to true)
11179      */
11180     select : function(index, scrollIntoView){
11181         this.selectedIndex = index;
11182         this.view.select(index);
11183         if(scrollIntoView !== false){
11184             var el = this.view.getNode(index);
11185             if(el){
11186                 //this.innerList.scrollChildIntoView(el, false);
11187                 
11188             }
11189         }
11190     },
11191
11192     // private
11193     selectNext : function(){
11194         var ct = this.store.getCount();
11195         if(ct > 0){
11196             if(this.selectedIndex == -1){
11197                 this.select(0);
11198             }else if(this.selectedIndex < ct-1){
11199                 this.select(this.selectedIndex+1);
11200             }
11201         }
11202     },
11203
11204     // private
11205     selectPrev : function(){
11206         var ct = this.store.getCount();
11207         if(ct > 0){
11208             if(this.selectedIndex == -1){
11209                 this.select(0);
11210             }else if(this.selectedIndex != 0){
11211                 this.select(this.selectedIndex-1);
11212             }
11213         }
11214     },
11215
11216     // private
11217     onKeyUp : function(e){
11218         if(this.editable !== false && !e.isSpecialKey()){
11219             this.lastKey = e.getKey();
11220             this.dqTask.delay(this.queryDelay);
11221         }
11222     },
11223
11224     // private
11225     validateBlur : function(){
11226         return !this.list || !this.list.isVisible();   
11227     },
11228
11229     // private
11230     initQuery : function(){
11231         this.doQuery(this.getRawValue());
11232     },
11233
11234     // private
11235     doForce : function(){
11236         if(this.inputEl().dom.value.length > 0){
11237             this.inputEl().dom.value =
11238                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
11239              
11240         }
11241     },
11242
11243     /**
11244      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
11245      * query allowing the query action to be canceled if needed.
11246      * @param {String} query The SQL query to execute
11247      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
11248      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
11249      * saved in the current store (defaults to false)
11250      */
11251     doQuery : function(q, forceAll){
11252         
11253         if(q === undefined || q === null){
11254             q = '';
11255         }
11256         var qe = {
11257             query: q,
11258             forceAll: forceAll,
11259             combo: this,
11260             cancel:false
11261         };
11262         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
11263             return false;
11264         }
11265         q = qe.query;
11266         
11267         forceAll = qe.forceAll;
11268         if(forceAll === true || (q.length >= this.minChars)){
11269             
11270             this.hasQuery = true;
11271             
11272             if(this.lastQuery != q || this.alwaysQuery){
11273                 this.lastQuery = q;
11274                 if(this.mode == 'local'){
11275                     this.selectedIndex = -1;
11276                     if(forceAll){
11277                         this.store.clearFilter();
11278                     }else{
11279                         this.store.filter(this.displayField, q);
11280                     }
11281                     this.onLoad();
11282                 }else{
11283                     this.store.baseParams[this.queryParam] = q;
11284                     
11285                     var options = {params : this.getParams(q)};
11286                     
11287                     if(this.loadNext){
11288                         options.add = true;
11289                         options.params.start = this.page * this.pageSize;
11290                     }
11291                     
11292                     this.store.load(options);
11293                     /*
11294                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
11295                      *  we should expand the list on onLoad
11296                      *  so command out it
11297                      */
11298 //                    this.expand();
11299                 }
11300             }else{
11301                 this.selectedIndex = -1;
11302                 this.onLoad();   
11303             }
11304         }
11305         
11306         this.loadNext = false;
11307     },
11308
11309     // private
11310     getParams : function(q){
11311         var p = {};
11312         //p[this.queryParam] = q;
11313         
11314         if(this.pageSize){
11315             p.start = 0;
11316             p.limit = this.pageSize;
11317         }
11318         return p;
11319     },
11320
11321     /**
11322      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
11323      */
11324     collapse : function(){
11325         if(!this.isExpanded()){
11326             return;
11327         }
11328         
11329         this.hasFocus = false;
11330         
11331         this.list.hide();
11332         
11333         if(this.tickable){
11334             this.okBtn.hide();
11335             this.cancelBtn.hide();
11336             this.trigger.show();
11337         }
11338         
11339         Roo.get(document).un('mousedown', this.collapseIf, this);
11340         Roo.get(document).un('mousewheel', this.collapseIf, this);
11341         if (!this.editable) {
11342             Roo.get(document).un('keydown', this.listKeyPress, this);
11343         }
11344         this.fireEvent('collapse', this);
11345     },
11346
11347     // private
11348     collapseIf : function(e){
11349         var in_combo  = e.within(this.el);
11350         var in_list =  e.within(this.list);
11351         
11352         if (in_combo || in_list) {
11353             //e.stopPropagation();
11354             return;
11355         }
11356         
11357         if(this.tickable){
11358             this.onTickableFooterButtonClick(e, false, false);
11359         }
11360
11361         this.collapse();
11362         
11363     },
11364
11365     /**
11366      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
11367      */
11368     expand : function(){
11369        
11370         if(this.isExpanded() || !this.hasFocus){
11371             return;
11372         }
11373          Roo.log('expand');
11374         this.list.alignTo(this.inputEl(), this.listAlign);
11375         this.list.show();
11376         
11377         if(this.tickable){
11378             
11379             this.tickItems = Roo.apply([], this.item);
11380             
11381             this.okBtn.show();
11382             this.cancelBtn.show();
11383             this.trigger.hide();
11384             
11385         }
11386         
11387         Roo.get(document).on('mousedown', this.collapseIf, this);
11388         Roo.get(document).on('mousewheel', this.collapseIf, this);
11389         if (!this.editable) {
11390             Roo.get(document).on('keydown', this.listKeyPress, this);
11391         }
11392         
11393         this.fireEvent('expand', this);
11394     },
11395
11396     // private
11397     // Implements the default empty TriggerField.onTriggerClick function
11398     onTriggerClick : function(e)
11399     {
11400         Roo.log('trigger click');
11401         
11402         if(this.disabled){
11403             return;
11404         }
11405         
11406         this.page = 0;
11407         this.loadNext = false;
11408         
11409         if(this.isExpanded()){
11410             this.collapse();
11411             if (!this.blockFocus) {
11412                 this.inputEl().focus();
11413             }
11414             
11415         }else {
11416             this.hasFocus = true;
11417             if(this.triggerAction == 'all') {
11418                 this.doQuery(this.allQuery, true);
11419             } else {
11420                 this.doQuery(this.getRawValue());
11421             }
11422             if (!this.blockFocus) {
11423                 this.inputEl().focus();
11424             }
11425         }
11426     },
11427     
11428     onTickableTriggerClick : function(e)
11429     {
11430         if(this.disabled){
11431             return;
11432         }
11433         
11434         this.page = 0;
11435         this.loadNext = false;
11436         this.hasFocus = true;
11437         
11438         if(this.triggerAction == 'all') {
11439             this.doQuery(this.allQuery, true);
11440         } else {
11441             this.doQuery(this.getRawValue());
11442         }
11443     },
11444     
11445     onSearchFieldClick : function(e)
11446     {
11447         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
11448             return;
11449         }
11450         
11451         this.page = 0;
11452         this.loadNext = false;
11453         this.hasFocus = true;
11454         
11455         if(this.triggerAction == 'all') {
11456             this.doQuery(this.allQuery, true);
11457         } else {
11458             this.doQuery(this.getRawValue());
11459         }
11460     },
11461     
11462     listKeyPress : function(e)
11463     {
11464         //Roo.log('listkeypress');
11465         // scroll to first matching element based on key pres..
11466         if (e.isSpecialKey()) {
11467             return false;
11468         }
11469         var k = String.fromCharCode(e.getKey()).toUpperCase();
11470         //Roo.log(k);
11471         var match  = false;
11472         var csel = this.view.getSelectedNodes();
11473         var cselitem = false;
11474         if (csel.length) {
11475             var ix = this.view.indexOf(csel[0]);
11476             cselitem  = this.store.getAt(ix);
11477             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
11478                 cselitem = false;
11479             }
11480             
11481         }
11482         
11483         this.store.each(function(v) { 
11484             if (cselitem) {
11485                 // start at existing selection.
11486                 if (cselitem.id == v.id) {
11487                     cselitem = false;
11488                 }
11489                 return true;
11490             }
11491                 
11492             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
11493                 match = this.store.indexOf(v);
11494                 return false;
11495             }
11496             return true;
11497         }, this);
11498         
11499         if (match === false) {
11500             return true; // no more action?
11501         }
11502         // scroll to?
11503         this.view.select(match);
11504         var sn = Roo.get(this.view.getSelectedNodes()[0])
11505         //sn.scrollIntoView(sn.dom.parentNode, false);
11506     },
11507     
11508     onViewScroll : function(e, t){
11509         
11510         if(this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
11511             return;
11512         }
11513         
11514         this.hasQuery = true;
11515         
11516         this.loading = this.list.select('.loading', true).first();
11517         
11518         if(this.loading === null){
11519             this.list.createChild({
11520                 tag: 'div',
11521                 cls: 'loading select2-more-results select2-active',
11522                 html: 'Loading more results...'
11523             })
11524             
11525             this.loading = this.list.select('.loading', true).first();
11526             
11527             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
11528             
11529             this.loading.hide();
11530         }
11531         
11532         this.loading.show();
11533         
11534         var _combo = this;
11535         
11536         this.page++;
11537         this.loadNext = true;
11538         
11539         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
11540         
11541         return;
11542     },
11543     
11544     addItem : function(o)
11545     {   
11546         var dv = ''; // display value
11547         
11548         if (this.displayField) {
11549             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11550         } else {
11551             // this is an error condition!!!
11552             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
11553         }
11554         
11555         if(!dv.length){
11556             return;
11557         }
11558         
11559         var choice = this.choices.createChild({
11560             tag: 'li',
11561             cls: 'select2-search-choice',
11562             cn: [
11563                 {
11564                     tag: 'div',
11565                     html: dv
11566                 },
11567                 {
11568                     tag: 'a',
11569                     href: '#',
11570                     cls: 'select2-search-choice-close',
11571                     tabindex: '-1'
11572                 }
11573             ]
11574             
11575         }, this.searchField);
11576         
11577         var close = choice.select('a.select2-search-choice-close', true).first()
11578         
11579         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
11580         
11581         this.item.push(o);
11582         
11583         this.lastData = o;
11584         
11585         this.syncValue();
11586         
11587         this.inputEl().dom.value = '';
11588         
11589     },
11590     
11591     onRemoveItem : function(e, _self, o)
11592     {
11593         e.preventDefault();
11594         var index = this.item.indexOf(o.data) * 1;
11595         
11596         if( index < 0){
11597             Roo.log('not this item?!');
11598             return;
11599         }
11600         
11601         this.item.splice(index, 1);
11602         o.item.remove();
11603         
11604         this.syncValue();
11605         
11606         this.fireEvent('remove', this, e);
11607         
11608     },
11609     
11610     syncValue : function()
11611     {
11612         if(!this.item.length){
11613             this.clearValue();
11614             return;
11615         }
11616             
11617         var value = [];
11618         var _this = this;
11619         Roo.each(this.item, function(i){
11620             if(_this.valueField){
11621                 value.push(i[_this.valueField]);
11622                 return;
11623             }
11624
11625             value.push(i);
11626         });
11627
11628         this.value = value.join(',');
11629
11630         if(this.hiddenField){
11631             this.hiddenField.dom.value = this.value;
11632         }
11633     },
11634     
11635     clearItem : function()
11636     {
11637         if(!this.multiple){
11638             return;
11639         }
11640         
11641         this.item = [];
11642         
11643         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
11644            c.remove();
11645         });
11646         
11647         this.syncValue();
11648     },
11649     
11650     inputEl: function ()
11651     {
11652         if(this.tickable){
11653             return this.searchField;
11654         }
11655         return this.el.select('input.form-control',true).first();
11656     },
11657     
11658     
11659     onTickableFooterButtonClick : function(e, btn, el)
11660     {
11661         e.preventDefault();
11662         
11663         if(btn && btn.name == 'cancel'){
11664             this.tickItems = Roo.apply([], this.item);
11665             this.collapse();
11666             return;
11667         }
11668         
11669         this.clearItem();
11670         
11671         var _this = this;
11672         
11673         Roo.each(this.tickItems, function(o){
11674             _this.addItem(o);
11675         });
11676         
11677         this.collapse();
11678         
11679     }
11680     
11681     
11682
11683     /** 
11684     * @cfg {Boolean} grow 
11685     * @hide 
11686     */
11687     /** 
11688     * @cfg {Number} growMin 
11689     * @hide 
11690     */
11691     /** 
11692     * @cfg {Number} growMax 
11693     * @hide 
11694     */
11695     /**
11696      * @hide
11697      * @method autoSize
11698      */
11699 });
11700 /*
11701  * Based on:
11702  * Ext JS Library 1.1.1
11703  * Copyright(c) 2006-2007, Ext JS, LLC.
11704  *
11705  * Originally Released Under LGPL - original licence link has changed is not relivant.
11706  *
11707  * Fork - LGPL
11708  * <script type="text/javascript">
11709  */
11710
11711 /**
11712  * @class Roo.View
11713  * @extends Roo.util.Observable
11714  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
11715  * This class also supports single and multi selection modes. <br>
11716  * Create a data model bound view:
11717  <pre><code>
11718  var store = new Roo.data.Store(...);
11719
11720  var view = new Roo.View({
11721     el : "my-element",
11722     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
11723  
11724     singleSelect: true,
11725     selectedClass: "ydataview-selected",
11726     store: store
11727  });
11728
11729  // listen for node click?
11730  view.on("click", function(vw, index, node, e){
11731  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
11732  });
11733
11734  // load XML data
11735  dataModel.load("foobar.xml");
11736  </code></pre>
11737  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
11738  * <br><br>
11739  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
11740  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
11741  * 
11742  * Note: old style constructor is still suported (container, template, config)
11743  * 
11744  * @constructor
11745  * Create a new View
11746  * @param {Object} config The config object
11747  * 
11748  */
11749 Roo.View = function(config, depreciated_tpl, depreciated_config){
11750     
11751     this.parent = false;
11752     
11753     if (typeof(depreciated_tpl) == 'undefined') {
11754         // new way.. - universal constructor.
11755         Roo.apply(this, config);
11756         this.el  = Roo.get(this.el);
11757     } else {
11758         // old format..
11759         this.el  = Roo.get(config);
11760         this.tpl = depreciated_tpl;
11761         Roo.apply(this, depreciated_config);
11762     }
11763     this.wrapEl  = this.el.wrap().wrap();
11764     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
11765     
11766     
11767     if(typeof(this.tpl) == "string"){
11768         this.tpl = new Roo.Template(this.tpl);
11769     } else {
11770         // support xtype ctors..
11771         this.tpl = new Roo.factory(this.tpl, Roo);
11772     }
11773     
11774     
11775     this.tpl.compile();
11776     
11777     /** @private */
11778     this.addEvents({
11779         /**
11780          * @event beforeclick
11781          * Fires before a click is processed. Returns false to cancel the default action.
11782          * @param {Roo.View} this
11783          * @param {Number} index The index of the target node
11784          * @param {HTMLElement} node The target node
11785          * @param {Roo.EventObject} e The raw event object
11786          */
11787             "beforeclick" : true,
11788         /**
11789          * @event click
11790          * Fires when a template node is clicked.
11791          * @param {Roo.View} this
11792          * @param {Number} index The index of the target node
11793          * @param {HTMLElement} node The target node
11794          * @param {Roo.EventObject} e The raw event object
11795          */
11796             "click" : true,
11797         /**
11798          * @event dblclick
11799          * Fires when a template node is double clicked.
11800          * @param {Roo.View} this
11801          * @param {Number} index The index of the target node
11802          * @param {HTMLElement} node The target node
11803          * @param {Roo.EventObject} e The raw event object
11804          */
11805             "dblclick" : true,
11806         /**
11807          * @event contextmenu
11808          * Fires when a template node is right clicked.
11809          * @param {Roo.View} this
11810          * @param {Number} index The index of the target node
11811          * @param {HTMLElement} node The target node
11812          * @param {Roo.EventObject} e The raw event object
11813          */
11814             "contextmenu" : true,
11815         /**
11816          * @event selectionchange
11817          * Fires when the selected nodes change.
11818          * @param {Roo.View} this
11819          * @param {Array} selections Array of the selected nodes
11820          */
11821             "selectionchange" : true,
11822     
11823         /**
11824          * @event beforeselect
11825          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
11826          * @param {Roo.View} this
11827          * @param {HTMLElement} node The node to be selected
11828          * @param {Array} selections Array of currently selected nodes
11829          */
11830             "beforeselect" : true,
11831         /**
11832          * @event preparedata
11833          * Fires on every row to render, to allow you to change the data.
11834          * @param {Roo.View} this
11835          * @param {Object} data to be rendered (change this)
11836          */
11837           "preparedata" : true
11838           
11839           
11840         });
11841
11842
11843
11844     this.el.on({
11845         "click": this.onClick,
11846         "dblclick": this.onDblClick,
11847         "contextmenu": this.onContextMenu,
11848         scope:this
11849     });
11850
11851     this.selections = [];
11852     this.nodes = [];
11853     this.cmp = new Roo.CompositeElementLite([]);
11854     if(this.store){
11855         this.store = Roo.factory(this.store, Roo.data);
11856         this.setStore(this.store, true);
11857     }
11858     
11859     if ( this.footer && this.footer.xtype) {
11860            
11861          var fctr = this.wrapEl.appendChild(document.createElement("div"));
11862         
11863         this.footer.dataSource = this.store
11864         this.footer.container = fctr;
11865         this.footer = Roo.factory(this.footer, Roo);
11866         fctr.insertFirst(this.el);
11867         
11868         // this is a bit insane - as the paging toolbar seems to detach the el..
11869 //        dom.parentNode.parentNode.parentNode
11870          // they get detached?
11871     }
11872     
11873     
11874     Roo.View.superclass.constructor.call(this);
11875     
11876     
11877 };
11878
11879 Roo.extend(Roo.View, Roo.util.Observable, {
11880     
11881      /**
11882      * @cfg {Roo.data.Store} store Data store to load data from.
11883      */
11884     store : false,
11885     
11886     /**
11887      * @cfg {String|Roo.Element} el The container element.
11888      */
11889     el : '',
11890     
11891     /**
11892      * @cfg {String|Roo.Template} tpl The template used by this View 
11893      */
11894     tpl : false,
11895     /**
11896      * @cfg {String} dataName the named area of the template to use as the data area
11897      *                          Works with domtemplates roo-name="name"
11898      */
11899     dataName: false,
11900     /**
11901      * @cfg {String} selectedClass The css class to add to selected nodes
11902      */
11903     selectedClass : "x-view-selected",
11904      /**
11905      * @cfg {String} emptyText The empty text to show when nothing is loaded.
11906      */
11907     emptyText : "",
11908     
11909     /**
11910      * @cfg {String} text to display on mask (default Loading)
11911      */
11912     mask : false,
11913     /**
11914      * @cfg {Boolean} multiSelect Allow multiple selection
11915      */
11916     multiSelect : false,
11917     /**
11918      * @cfg {Boolean} singleSelect Allow single selection
11919      */
11920     singleSelect:  false,
11921     
11922     /**
11923      * @cfg {Boolean} toggleSelect - selecting 
11924      */
11925     toggleSelect : false,
11926     
11927     /**
11928      * @cfg {Boolean} tickable - selecting 
11929      */
11930     tickable : false,
11931     
11932     /**
11933      * Returns the element this view is bound to.
11934      * @return {Roo.Element}
11935      */
11936     getEl : function(){
11937         return this.wrapEl;
11938     },
11939     
11940     
11941
11942     /**
11943      * Refreshes the view. - called by datachanged on the store. - do not call directly.
11944      */
11945     refresh : function(){
11946         Roo.log('refresh');
11947         var t = this.tpl;
11948         
11949         // if we are using something like 'domtemplate', then
11950         // the what gets used is:
11951         // t.applySubtemplate(NAME, data, wrapping data..)
11952         // the outer template then get' applied with
11953         //     the store 'extra data'
11954         // and the body get's added to the
11955         //      roo-name="data" node?
11956         //      <span class='roo-tpl-{name}'></span> ?????
11957         
11958         
11959         
11960         this.clearSelections();
11961         this.el.update("");
11962         var html = [];
11963         var records = this.store.getRange();
11964         if(records.length < 1) {
11965             
11966             // is this valid??  = should it render a template??
11967             
11968             this.el.update(this.emptyText);
11969             return;
11970         }
11971         var el = this.el;
11972         if (this.dataName) {
11973             this.el.update(t.apply(this.store.meta)); //????
11974             el = this.el.child('.roo-tpl-' + this.dataName);
11975         }
11976         
11977         for(var i = 0, len = records.length; i < len; i++){
11978             var data = this.prepareData(records[i].data, i, records[i]);
11979             this.fireEvent("preparedata", this, data, i, records[i]);
11980             
11981             var d = Roo.apply({}, data);
11982             
11983             if(this.tickable){
11984                 Roo.apply(d, {'roo-id' : Roo.id()});
11985                 
11986                 var _this = this;
11987             
11988                 Roo.each(this.parent.item, function(item){
11989                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
11990                         return;
11991                     }
11992                     Roo.apply(d, {'roo-data-checked' : 'checked'});
11993                 });
11994             }
11995             
11996             html[html.length] = Roo.util.Format.trim(
11997                 this.dataName ?
11998                     t.applySubtemplate(this.dataName, d, this.store.meta) :
11999                     t.apply(d)
12000             );
12001         }
12002         
12003         
12004         
12005         el.update(html.join(""));
12006         this.nodes = el.dom.childNodes;
12007         this.updateIndexes(0);
12008     },
12009     
12010
12011     /**
12012      * Function to override to reformat the data that is sent to
12013      * the template for each node.
12014      * DEPRICATED - use the preparedata event handler.
12015      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
12016      * a JSON object for an UpdateManager bound view).
12017      */
12018     prepareData : function(data, index, record)
12019     {
12020         this.fireEvent("preparedata", this, data, index, record);
12021         return data;
12022     },
12023
12024     onUpdate : function(ds, record){
12025          Roo.log('on update');   
12026         this.clearSelections();
12027         var index = this.store.indexOf(record);
12028         var n = this.nodes[index];
12029         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
12030         n.parentNode.removeChild(n);
12031         this.updateIndexes(index, index);
12032     },
12033
12034     
12035     
12036 // --------- FIXME     
12037     onAdd : function(ds, records, index)
12038     {
12039         Roo.log(['on Add', ds, records, index] );        
12040         this.clearSelections();
12041         if(this.nodes.length == 0){
12042             this.refresh();
12043             return;
12044         }
12045         var n = this.nodes[index];
12046         for(var i = 0, len = records.length; i < len; i++){
12047             var d = this.prepareData(records[i].data, i, records[i]);
12048             if(n){
12049                 this.tpl.insertBefore(n, d);
12050             }else{
12051                 
12052                 this.tpl.append(this.el, d);
12053             }
12054         }
12055         this.updateIndexes(index);
12056     },
12057
12058     onRemove : function(ds, record, index){
12059         Roo.log('onRemove');
12060         this.clearSelections();
12061         var el = this.dataName  ?
12062             this.el.child('.roo-tpl-' + this.dataName) :
12063             this.el; 
12064         
12065         el.dom.removeChild(this.nodes[index]);
12066         this.updateIndexes(index);
12067     },
12068
12069     /**
12070      * Refresh an individual node.
12071      * @param {Number} index
12072      */
12073     refreshNode : function(index){
12074         this.onUpdate(this.store, this.store.getAt(index));
12075     },
12076
12077     updateIndexes : function(startIndex, endIndex){
12078         var ns = this.nodes;
12079         startIndex = startIndex || 0;
12080         endIndex = endIndex || ns.length - 1;
12081         for(var i = startIndex; i <= endIndex; i++){
12082             ns[i].nodeIndex = i;
12083         }
12084     },
12085
12086     /**
12087      * Changes the data store this view uses and refresh the view.
12088      * @param {Store} store
12089      */
12090     setStore : function(store, initial){
12091         if(!initial && this.store){
12092             this.store.un("datachanged", this.refresh);
12093             this.store.un("add", this.onAdd);
12094             this.store.un("remove", this.onRemove);
12095             this.store.un("update", this.onUpdate);
12096             this.store.un("clear", this.refresh);
12097             this.store.un("beforeload", this.onBeforeLoad);
12098             this.store.un("load", this.onLoad);
12099             this.store.un("loadexception", this.onLoad);
12100         }
12101         if(store){
12102           
12103             store.on("datachanged", this.refresh, this);
12104             store.on("add", this.onAdd, this);
12105             store.on("remove", this.onRemove, this);
12106             store.on("update", this.onUpdate, this);
12107             store.on("clear", this.refresh, this);
12108             store.on("beforeload", this.onBeforeLoad, this);
12109             store.on("load", this.onLoad, this);
12110             store.on("loadexception", this.onLoad, this);
12111         }
12112         
12113         if(store){
12114             this.refresh();
12115         }
12116     },
12117     /**
12118      * onbeforeLoad - masks the loading area.
12119      *
12120      */
12121     onBeforeLoad : function(store,opts)
12122     {
12123          Roo.log('onBeforeLoad');   
12124         if (!opts.add) {
12125             this.el.update("");
12126         }
12127         this.el.mask(this.mask ? this.mask : "Loading" ); 
12128     },
12129     onLoad : function ()
12130     {
12131         this.el.unmask();
12132     },
12133     
12134
12135     /**
12136      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
12137      * @param {HTMLElement} node
12138      * @return {HTMLElement} The template node
12139      */
12140     findItemFromChild : function(node){
12141         var el = this.dataName  ?
12142             this.el.child('.roo-tpl-' + this.dataName,true) :
12143             this.el.dom; 
12144         
12145         if(!node || node.parentNode == el){
12146                     return node;
12147             }
12148             var p = node.parentNode;
12149             while(p && p != el){
12150             if(p.parentNode == el){
12151                 return p;
12152             }
12153             p = p.parentNode;
12154         }
12155             return null;
12156     },
12157
12158     /** @ignore */
12159     onClick : function(e){
12160         var item = this.findItemFromChild(e.getTarget());
12161         if(item){
12162             var index = this.indexOf(item);
12163             if(this.onItemClick(item, index, e) !== false){
12164                 this.fireEvent("click", this, index, item, e);
12165             }
12166         }else{
12167             this.clearSelections();
12168         }
12169     },
12170
12171     /** @ignore */
12172     onContextMenu : function(e){
12173         var item = this.findItemFromChild(e.getTarget());
12174         if(item){
12175             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
12176         }
12177     },
12178
12179     /** @ignore */
12180     onDblClick : function(e){
12181         var item = this.findItemFromChild(e.getTarget());
12182         if(item){
12183             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
12184         }
12185     },
12186
12187     onItemClick : function(item, index, e)
12188     {
12189         if(this.fireEvent("beforeclick", this, index, item, e) === false){
12190             return false;
12191         }
12192         if (this.toggleSelect) {
12193             var m = this.isSelected(item) ? 'unselect' : 'select';
12194             Roo.log(m);
12195             var _t = this;
12196             _t[m](item, true, false);
12197             return true;
12198         }
12199         if(this.multiSelect || this.singleSelect){
12200             if(this.multiSelect && e.shiftKey && this.lastSelection){
12201                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
12202             }else{
12203                 this.select(item, this.multiSelect && e.ctrlKey);
12204                 this.lastSelection = item;
12205             }
12206             
12207             if(!this.tickable){
12208                 e.preventDefault();
12209             }
12210             
12211         }
12212         return true;
12213     },
12214
12215     /**
12216      * Get the number of selected nodes.
12217      * @return {Number}
12218      */
12219     getSelectionCount : function(){
12220         return this.selections.length;
12221     },
12222
12223     /**
12224      * Get the currently selected nodes.
12225      * @return {Array} An array of HTMLElements
12226      */
12227     getSelectedNodes : function(){
12228         return this.selections;
12229     },
12230
12231     /**
12232      * Get the indexes of the selected nodes.
12233      * @return {Array}
12234      */
12235     getSelectedIndexes : function(){
12236         var indexes = [], s = this.selections;
12237         for(var i = 0, len = s.length; i < len; i++){
12238             indexes.push(s[i].nodeIndex);
12239         }
12240         return indexes;
12241     },
12242
12243     /**
12244      * Clear all selections
12245      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
12246      */
12247     clearSelections : function(suppressEvent){
12248         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
12249             this.cmp.elements = this.selections;
12250             this.cmp.removeClass(this.selectedClass);
12251             this.selections = [];
12252             if(!suppressEvent){
12253                 this.fireEvent("selectionchange", this, this.selections);
12254             }
12255         }
12256     },
12257
12258     /**
12259      * Returns true if the passed node is selected
12260      * @param {HTMLElement/Number} node The node or node index
12261      * @return {Boolean}
12262      */
12263     isSelected : function(node){
12264         var s = this.selections;
12265         if(s.length < 1){
12266             return false;
12267         }
12268         node = this.getNode(node);
12269         return s.indexOf(node) !== -1;
12270     },
12271
12272     /**
12273      * Selects nodes.
12274      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
12275      * @param {Boolean} keepExisting (optional) true to keep existing selections
12276      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12277      */
12278     select : function(nodeInfo, keepExisting, suppressEvent){
12279         if(nodeInfo instanceof Array){
12280             if(!keepExisting){
12281                 this.clearSelections(true);
12282             }
12283             for(var i = 0, len = nodeInfo.length; i < len; i++){
12284                 this.select(nodeInfo[i], true, true);
12285             }
12286             return;
12287         } 
12288         var node = this.getNode(nodeInfo);
12289         if(!node || this.isSelected(node)){
12290             return; // already selected.
12291         }
12292         if(!keepExisting){
12293             this.clearSelections(true);
12294         }
12295         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
12296             Roo.fly(node).addClass(this.selectedClass);
12297             this.selections.push(node);
12298             if(!suppressEvent){
12299                 this.fireEvent("selectionchange", this, this.selections);
12300             }
12301         }
12302         
12303         
12304     },
12305       /**
12306      * Unselects nodes.
12307      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
12308      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
12309      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12310      */
12311     unselect : function(nodeInfo, keepExisting, suppressEvent)
12312     {
12313         if(nodeInfo instanceof Array){
12314             Roo.each(this.selections, function(s) {
12315                 this.unselect(s, nodeInfo);
12316             }, this);
12317             return;
12318         }
12319         var node = this.getNode(nodeInfo);
12320         if(!node || !this.isSelected(node)){
12321             Roo.log("not selected");
12322             return; // not selected.
12323         }
12324         // fireevent???
12325         var ns = [];
12326         Roo.each(this.selections, function(s) {
12327             if (s == node ) {
12328                 Roo.fly(node).removeClass(this.selectedClass);
12329
12330                 return;
12331             }
12332             ns.push(s);
12333         },this);
12334         
12335         this.selections= ns;
12336         this.fireEvent("selectionchange", this, this.selections);
12337     },
12338
12339     /**
12340      * Gets a template node.
12341      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12342      * @return {HTMLElement} The node or null if it wasn't found
12343      */
12344     getNode : function(nodeInfo){
12345         if(typeof nodeInfo == "string"){
12346             return document.getElementById(nodeInfo);
12347         }else if(typeof nodeInfo == "number"){
12348             return this.nodes[nodeInfo];
12349         }
12350         return nodeInfo;
12351     },
12352
12353     /**
12354      * Gets a range template nodes.
12355      * @param {Number} startIndex
12356      * @param {Number} endIndex
12357      * @return {Array} An array of nodes
12358      */
12359     getNodes : function(start, end){
12360         var ns = this.nodes;
12361         start = start || 0;
12362         end = typeof end == "undefined" ? ns.length - 1 : end;
12363         var nodes = [];
12364         if(start <= end){
12365             for(var i = start; i <= end; i++){
12366                 nodes.push(ns[i]);
12367             }
12368         } else{
12369             for(var i = start; i >= end; i--){
12370                 nodes.push(ns[i]);
12371             }
12372         }
12373         return nodes;
12374     },
12375
12376     /**
12377      * Finds the index of the passed node
12378      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12379      * @return {Number} The index of the node or -1
12380      */
12381     indexOf : function(node){
12382         node = this.getNode(node);
12383         if(typeof node.nodeIndex == "number"){
12384             return node.nodeIndex;
12385         }
12386         var ns = this.nodes;
12387         for(var i = 0, len = ns.length; i < len; i++){
12388             if(ns[i] == node){
12389                 return i;
12390             }
12391         }
12392         return -1;
12393     }
12394 });
12395 /*
12396  * - LGPL
12397  *
12398  * based on jquery fullcalendar
12399  * 
12400  */
12401
12402 Roo.bootstrap = Roo.bootstrap || {};
12403 /**
12404  * @class Roo.bootstrap.Calendar
12405  * @extends Roo.bootstrap.Component
12406  * Bootstrap Calendar class
12407  * @cfg {Boolean} loadMask (true|false) default false
12408  * @cfg {Object} header generate the user specific header of the calendar, default false
12409
12410  * @constructor
12411  * Create a new Container
12412  * @param {Object} config The config object
12413  */
12414
12415
12416
12417 Roo.bootstrap.Calendar = function(config){
12418     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
12419      this.addEvents({
12420         /**
12421              * @event select
12422              * Fires when a date is selected
12423              * @param {DatePicker} this
12424              * @param {Date} date The selected date
12425              */
12426         'select': true,
12427         /**
12428              * @event monthchange
12429              * Fires when the displayed month changes 
12430              * @param {DatePicker} this
12431              * @param {Date} date The selected month
12432              */
12433         'monthchange': true,
12434         /**
12435              * @event evententer
12436              * Fires when mouse over an event
12437              * @param {Calendar} this
12438              * @param {event} Event
12439              */
12440         'evententer': true,
12441         /**
12442              * @event eventleave
12443              * Fires when the mouse leaves an
12444              * @param {Calendar} this
12445              * @param {event}
12446              */
12447         'eventleave': true,
12448         /**
12449              * @event eventclick
12450              * Fires when the mouse click an
12451              * @param {Calendar} this
12452              * @param {event}
12453              */
12454         'eventclick': true
12455         
12456     });
12457
12458 };
12459
12460 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
12461     
12462      /**
12463      * @cfg {Number} startDay
12464      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
12465      */
12466     startDay : 0,
12467     
12468     loadMask : false,
12469     
12470     header : false,
12471       
12472     getAutoCreate : function(){
12473         
12474         
12475         var fc_button = function(name, corner, style, content ) {
12476             return Roo.apply({},{
12477                 tag : 'span',
12478                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
12479                          (corner.length ?
12480                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
12481                             ''
12482                         ),
12483                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
12484                 unselectable: 'on'
12485             });
12486         };
12487         
12488         var header = {};
12489         
12490         if(!this.header){
12491             header = {
12492                 tag : 'table',
12493                 cls : 'fc-header',
12494                 style : 'width:100%',
12495                 cn : [
12496                     {
12497                         tag: 'tr',
12498                         cn : [
12499                             {
12500                                 tag : 'td',
12501                                 cls : 'fc-header-left',
12502                                 cn : [
12503                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
12504                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
12505                                     { tag: 'span', cls: 'fc-header-space' },
12506                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
12507
12508
12509                                 ]
12510                             },
12511
12512                             {
12513                                 tag : 'td',
12514                                 cls : 'fc-header-center',
12515                                 cn : [
12516                                     {
12517                                         tag: 'span',
12518                                         cls: 'fc-header-title',
12519                                         cn : {
12520                                             tag: 'H2',
12521                                             html : 'month / year'
12522                                         }
12523                                     }
12524
12525                                 ]
12526                             },
12527                             {
12528                                 tag : 'td',
12529                                 cls : 'fc-header-right',
12530                                 cn : [
12531                               /*      fc_button('month', 'left', '', 'month' ),
12532                                     fc_button('week', '', '', 'week' ),
12533                                     fc_button('day', 'right', '', 'day' )
12534                                 */    
12535
12536                                 ]
12537                             }
12538
12539                         ]
12540                     }
12541                 ]
12542             };
12543         }
12544         
12545         header = this.header;
12546         
12547        
12548         var cal_heads = function() {
12549             var ret = [];
12550             // fixme - handle this.
12551             
12552             for (var i =0; i < Date.dayNames.length; i++) {
12553                 var d = Date.dayNames[i];
12554                 ret.push({
12555                     tag: 'th',
12556                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
12557                     html : d.substring(0,3)
12558                 });
12559                 
12560             }
12561             ret[0].cls += ' fc-first';
12562             ret[6].cls += ' fc-last';
12563             return ret;
12564         };
12565         var cal_cell = function(n) {
12566             return  {
12567                 tag: 'td',
12568                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
12569                 cn : [
12570                     {
12571                         cn : [
12572                             {
12573                                 cls: 'fc-day-number',
12574                                 html: 'D'
12575                             },
12576                             {
12577                                 cls: 'fc-day-content',
12578                              
12579                                 cn : [
12580                                      {
12581                                         style: 'position: relative;' // height: 17px;
12582                                     }
12583                                 ]
12584                             }
12585                             
12586                             
12587                         ]
12588                     }
12589                 ]
12590                 
12591             }
12592         };
12593         var cal_rows = function() {
12594             
12595             var ret = []
12596             for (var r = 0; r < 6; r++) {
12597                 var row= {
12598                     tag : 'tr',
12599                     cls : 'fc-week',
12600                     cn : []
12601                 };
12602                 
12603                 for (var i =0; i < Date.dayNames.length; i++) {
12604                     var d = Date.dayNames[i];
12605                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
12606
12607                 }
12608                 row.cn[0].cls+=' fc-first';
12609                 row.cn[0].cn[0].style = 'min-height:90px';
12610                 row.cn[6].cls+=' fc-last';
12611                 ret.push(row);
12612                 
12613             }
12614             ret[0].cls += ' fc-first';
12615             ret[4].cls += ' fc-prev-last';
12616             ret[5].cls += ' fc-last';
12617             return ret;
12618             
12619         };
12620         
12621         var cal_table = {
12622             tag: 'table',
12623             cls: 'fc-border-separate',
12624             style : 'width:100%',
12625             cellspacing  : 0,
12626             cn : [
12627                 { 
12628                     tag: 'thead',
12629                     cn : [
12630                         { 
12631                             tag: 'tr',
12632                             cls : 'fc-first fc-last',
12633                             cn : cal_heads()
12634                         }
12635                     ]
12636                 },
12637                 { 
12638                     tag: 'tbody',
12639                     cn : cal_rows()
12640                 }
12641                   
12642             ]
12643         };
12644          
12645          var cfg = {
12646             cls : 'fc fc-ltr',
12647             cn : [
12648                 header,
12649                 {
12650                     cls : 'fc-content',
12651                     style : "position: relative;",
12652                     cn : [
12653                         {
12654                             cls : 'fc-view fc-view-month fc-grid',
12655                             style : 'position: relative',
12656                             unselectable : 'on',
12657                             cn : [
12658                                 {
12659                                     cls : 'fc-event-container',
12660                                     style : 'position:absolute;z-index:8;top:0;left:0;'
12661                                 },
12662                                 cal_table
12663                             ]
12664                         }
12665                     ]
12666     
12667                 }
12668            ] 
12669             
12670         };
12671         
12672          
12673         
12674         return cfg;
12675     },
12676     
12677     
12678     initEvents : function()
12679     {
12680         if(!this.store){
12681             throw "can not find store for calendar";
12682         }
12683         
12684         var mark = {
12685             tag: "div",
12686             cls:"x-dlg-mask",
12687             style: "text-align:center",
12688             cn: [
12689                 {
12690                     tag: "div",
12691                     style: "background-color:white;width:50%;margin:250 auto",
12692                     cn: [
12693                         {
12694                             tag: "img",
12695                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
12696                         },
12697                         {
12698                             tag: "span",
12699                             html: "Loading"
12700                         }
12701                         
12702                     ]
12703                 }
12704             ]
12705         }
12706         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
12707         
12708         var size = this.el.select('.fc-content', true).first().getSize();
12709         this.maskEl.setSize(size.width, size.height);
12710         this.maskEl.enableDisplayMode("block");
12711         if(!this.loadMask){
12712             this.maskEl.hide();
12713         }
12714         
12715         this.store = Roo.factory(this.store, Roo.data);
12716         this.store.on('load', this.onLoad, this);
12717         this.store.on('beforeload', this.onBeforeLoad, this);
12718         
12719         this.resize();
12720         
12721         this.cells = this.el.select('.fc-day',true);
12722         //Roo.log(this.cells);
12723         this.textNodes = this.el.query('.fc-day-number');
12724         this.cells.addClassOnOver('fc-state-hover');
12725         
12726         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
12727         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
12728         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
12729         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
12730         
12731         this.on('monthchange', this.onMonthChange, this);
12732         
12733         this.update(new Date().clearTime());
12734     },
12735     
12736     resize : function() {
12737         var sz  = this.el.getSize();
12738         
12739         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
12740         this.el.select('.fc-day-content div',true).setHeight(34);
12741     },
12742     
12743     
12744     // private
12745     showPrevMonth : function(e){
12746         this.update(this.activeDate.add("mo", -1));
12747     },
12748     showToday : function(e){
12749         this.update(new Date().clearTime());
12750     },
12751     // private
12752     showNextMonth : function(e){
12753         this.update(this.activeDate.add("mo", 1));
12754     },
12755
12756     // private
12757     showPrevYear : function(){
12758         this.update(this.activeDate.add("y", -1));
12759     },
12760
12761     // private
12762     showNextYear : function(){
12763         this.update(this.activeDate.add("y", 1));
12764     },
12765
12766     
12767    // private
12768     update : function(date)
12769     {
12770         var vd = this.activeDate;
12771         this.activeDate = date;
12772 //        if(vd && this.el){
12773 //            var t = date.getTime();
12774 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
12775 //                Roo.log('using add remove');
12776 //                
12777 //                this.fireEvent('monthchange', this, date);
12778 //                
12779 //                this.cells.removeClass("fc-state-highlight");
12780 //                this.cells.each(function(c){
12781 //                   if(c.dateValue == t){
12782 //                       c.addClass("fc-state-highlight");
12783 //                       setTimeout(function(){
12784 //                            try{c.dom.firstChild.focus();}catch(e){}
12785 //                       }, 50);
12786 //                       return false;
12787 //                   }
12788 //                   return true;
12789 //                });
12790 //                return;
12791 //            }
12792 //        }
12793         
12794         var days = date.getDaysInMonth();
12795         
12796         var firstOfMonth = date.getFirstDateOfMonth();
12797         var startingPos = firstOfMonth.getDay()-this.startDay;
12798         
12799         if(startingPos < this.startDay){
12800             startingPos += 7;
12801         }
12802         
12803         var pm = date.add(Date.MONTH, -1);
12804         var prevStart = pm.getDaysInMonth()-startingPos;
12805 //        
12806         this.cells = this.el.select('.fc-day',true);
12807         this.textNodes = this.el.query('.fc-day-number');
12808         this.cells.addClassOnOver('fc-state-hover');
12809         
12810         var cells = this.cells.elements;
12811         var textEls = this.textNodes;
12812         
12813         Roo.each(cells, function(cell){
12814             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
12815         });
12816         
12817         days += startingPos;
12818
12819         // convert everything to numbers so it's fast
12820         var day = 86400000;
12821         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
12822         //Roo.log(d);
12823         //Roo.log(pm);
12824         //Roo.log(prevStart);
12825         
12826         var today = new Date().clearTime().getTime();
12827         var sel = date.clearTime().getTime();
12828         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
12829         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
12830         var ddMatch = this.disabledDatesRE;
12831         var ddText = this.disabledDatesText;
12832         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
12833         var ddaysText = this.disabledDaysText;
12834         var format = this.format;
12835         
12836         var setCellClass = function(cal, cell){
12837             cell.row = 0;
12838             cell.events = [];
12839             cell.more = [];
12840             //Roo.log('set Cell Class');
12841             cell.title = "";
12842             var t = d.getTime();
12843             
12844             //Roo.log(d);
12845             
12846             cell.dateValue = t;
12847             if(t == today){
12848                 cell.className += " fc-today";
12849                 cell.className += " fc-state-highlight";
12850                 cell.title = cal.todayText;
12851             }
12852             if(t == sel){
12853                 // disable highlight in other month..
12854                 //cell.className += " fc-state-highlight";
12855                 
12856             }
12857             // disabling
12858             if(t < min) {
12859                 cell.className = " fc-state-disabled";
12860                 cell.title = cal.minText;
12861                 return;
12862             }
12863             if(t > max) {
12864                 cell.className = " fc-state-disabled";
12865                 cell.title = cal.maxText;
12866                 return;
12867             }
12868             if(ddays){
12869                 if(ddays.indexOf(d.getDay()) != -1){
12870                     cell.title = ddaysText;
12871                     cell.className = " fc-state-disabled";
12872                 }
12873             }
12874             if(ddMatch && format){
12875                 var fvalue = d.dateFormat(format);
12876                 if(ddMatch.test(fvalue)){
12877                     cell.title = ddText.replace("%0", fvalue);
12878                     cell.className = " fc-state-disabled";
12879                 }
12880             }
12881             
12882             if (!cell.initialClassName) {
12883                 cell.initialClassName = cell.dom.className;
12884             }
12885             
12886             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
12887         };
12888
12889         var i = 0;
12890         
12891         for(; i < startingPos; i++) {
12892             textEls[i].innerHTML = (++prevStart);
12893             d.setDate(d.getDate()+1);
12894             
12895             cells[i].className = "fc-past fc-other-month";
12896             setCellClass(this, cells[i]);
12897         }
12898         
12899         var intDay = 0;
12900         
12901         for(; i < days; i++){
12902             intDay = i - startingPos + 1;
12903             textEls[i].innerHTML = (intDay);
12904             d.setDate(d.getDate()+1);
12905             
12906             cells[i].className = ''; // "x-date-active";
12907             setCellClass(this, cells[i]);
12908         }
12909         var extraDays = 0;
12910         
12911         for(; i < 42; i++) {
12912             textEls[i].innerHTML = (++extraDays);
12913             d.setDate(d.getDate()+1);
12914             
12915             cells[i].className = "fc-future fc-other-month";
12916             setCellClass(this, cells[i]);
12917         }
12918         
12919         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
12920         
12921         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
12922         
12923         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
12924         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
12925         
12926         if(totalRows != 6){
12927             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
12928             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
12929         }
12930         
12931         this.fireEvent('monthchange', this, date);
12932         
12933         
12934         /*
12935         if(!this.internalRender){
12936             var main = this.el.dom.firstChild;
12937             var w = main.offsetWidth;
12938             this.el.setWidth(w + this.el.getBorderWidth("lr"));
12939             Roo.fly(main).setWidth(w);
12940             this.internalRender = true;
12941             // opera does not respect the auto grow header center column
12942             // then, after it gets a width opera refuses to recalculate
12943             // without a second pass
12944             if(Roo.isOpera && !this.secondPass){
12945                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
12946                 this.secondPass = true;
12947                 this.update.defer(10, this, [date]);
12948             }
12949         }
12950         */
12951         
12952     },
12953     
12954     findCell : function(dt) {
12955         dt = dt.clearTime().getTime();
12956         var ret = false;
12957         this.cells.each(function(c){
12958             //Roo.log("check " +c.dateValue + '?=' + dt);
12959             if(c.dateValue == dt){
12960                 ret = c;
12961                 return false;
12962             }
12963             return true;
12964         });
12965         
12966         return ret;
12967     },
12968     
12969     findCells : function(ev) {
12970         var s = ev.start.clone().clearTime().getTime();
12971        // Roo.log(s);
12972         var e= ev.end.clone().clearTime().getTime();
12973        // Roo.log(e);
12974         var ret = [];
12975         this.cells.each(function(c){
12976              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
12977             
12978             if(c.dateValue > e){
12979                 return ;
12980             }
12981             if(c.dateValue < s){
12982                 return ;
12983             }
12984             ret.push(c);
12985         });
12986         
12987         return ret;    
12988     },
12989     
12990 //    findBestRow: function(cells)
12991 //    {
12992 //        var ret = 0;
12993 //        
12994 //        for (var i =0 ; i < cells.length;i++) {
12995 //            ret  = Math.max(cells[i].rows || 0,ret);
12996 //        }
12997 //        return ret;
12998 //        
12999 //    },
13000     
13001     
13002     addItem : function(ev)
13003     {
13004         // look for vertical location slot in
13005         var cells = this.findCells(ev);
13006         
13007 //        ev.row = this.findBestRow(cells);
13008         
13009         // work out the location.
13010         
13011         var crow = false;
13012         var rows = [];
13013         for(var i =0; i < cells.length; i++) {
13014             
13015             cells[i].row = cells[0].row;
13016             
13017             if(i == 0){
13018                 cells[i].row = cells[i].row + 1;
13019             }
13020             
13021             if (!crow) {
13022                 crow = {
13023                     start : cells[i],
13024                     end :  cells[i]
13025                 };
13026                 continue;
13027             }
13028             if (crow.start.getY() == cells[i].getY()) {
13029                 // on same row.
13030                 crow.end = cells[i];
13031                 continue;
13032             }
13033             // different row.
13034             rows.push(crow);
13035             crow = {
13036                 start: cells[i],
13037                 end : cells[i]
13038             };
13039             
13040         }
13041         
13042         rows.push(crow);
13043         ev.els = [];
13044         ev.rows = rows;
13045         ev.cells = cells;
13046         
13047         cells[0].events.push(ev);
13048         
13049         this.calevents.push(ev);
13050     },
13051     
13052     clearEvents: function() {
13053         
13054         if(!this.calevents){
13055             return;
13056         }
13057         
13058         Roo.each(this.cells.elements, function(c){
13059             c.row = 0;
13060             c.events = [];
13061             c.more = [];
13062         });
13063         
13064         Roo.each(this.calevents, function(e) {
13065             Roo.each(e.els, function(el) {
13066                 el.un('mouseenter' ,this.onEventEnter, this);
13067                 el.un('mouseleave' ,this.onEventLeave, this);
13068                 el.remove();
13069             },this);
13070         },this);
13071         
13072         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
13073             e.remove();
13074         });
13075         
13076     },
13077     
13078     renderEvents: function()
13079     {   
13080         var _this = this;
13081         
13082         this.cells.each(function(c) {
13083             
13084             if(c.row < 5){
13085                 return;
13086             }
13087             
13088             var ev = c.events;
13089             
13090             var r = 4;
13091             if(c.row != c.events.length){
13092                 r = 4 - (4 - (c.row - c.events.length));
13093             }
13094             
13095             c.events = ev.slice(0, r);
13096             c.more = ev.slice(r);
13097             
13098             if(c.more.length && c.more.length == 1){
13099                 c.events.push(c.more.pop());
13100             }
13101             
13102             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
13103             
13104         });
13105             
13106         this.cells.each(function(c) {
13107             
13108             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
13109             
13110             
13111             for (var e = 0; e < c.events.length; e++){
13112                 var ev = c.events[e];
13113                 var rows = ev.rows;
13114                 
13115                 for(var i = 0; i < rows.length; i++) {
13116                 
13117                     // how many rows should it span..
13118
13119                     var  cfg = {
13120                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
13121                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
13122
13123                         unselectable : "on",
13124                         cn : [
13125                             {
13126                                 cls: 'fc-event-inner',
13127                                 cn : [
13128     //                                {
13129     //                                  tag:'span',
13130     //                                  cls: 'fc-event-time',
13131     //                                  html : cells.length > 1 ? '' : ev.time
13132     //                                },
13133                                     {
13134                                       tag:'span',
13135                                       cls: 'fc-event-title',
13136                                       html : String.format('{0}', ev.title)
13137                                     }
13138
13139
13140                                 ]
13141                             },
13142                             {
13143                                 cls: 'ui-resizable-handle ui-resizable-e',
13144                                 html : '&nbsp;&nbsp;&nbsp'
13145                             }
13146
13147                         ]
13148                     };
13149
13150                     if (i == 0) {
13151                         cfg.cls += ' fc-event-start';
13152                     }
13153                     if ((i+1) == rows.length) {
13154                         cfg.cls += ' fc-event-end';
13155                     }
13156
13157                     var ctr = _this.el.select('.fc-event-container',true).first();
13158                     var cg = ctr.createChild(cfg);
13159
13160                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
13161                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
13162
13163                     var r = (c.more.length) ? 1 : 0;
13164                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
13165                     cg.setWidth(ebox.right - sbox.x -2);
13166
13167                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
13168                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
13169                     cg.on('click', _this.onEventClick, _this, ev);
13170
13171                     ev.els.push(cg);
13172                     
13173                 }
13174                 
13175             }
13176             
13177             
13178             if(c.more.length){
13179                 var  cfg = {
13180                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
13181                     style : 'position: absolute',
13182                     unselectable : "on",
13183                     cn : [
13184                         {
13185                             cls: 'fc-event-inner',
13186                             cn : [
13187                                 {
13188                                   tag:'span',
13189                                   cls: 'fc-event-title',
13190                                   html : 'More'
13191                                 }
13192
13193
13194                             ]
13195                         },
13196                         {
13197                             cls: 'ui-resizable-handle ui-resizable-e',
13198                             html : '&nbsp;&nbsp;&nbsp'
13199                         }
13200
13201                     ]
13202                 };
13203
13204                 var ctr = _this.el.select('.fc-event-container',true).first();
13205                 var cg = ctr.createChild(cfg);
13206
13207                 var sbox = c.select('.fc-day-content',true).first().getBox();
13208                 var ebox = c.select('.fc-day-content',true).first().getBox();
13209                 //Roo.log(cg);
13210                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
13211                 cg.setWidth(ebox.right - sbox.x -2);
13212
13213                 cg.on('click', _this.onMoreEventClick, _this, c.more);
13214                 
13215             }
13216             
13217         });
13218         
13219         
13220         
13221     },
13222     
13223     onEventEnter: function (e, el,event,d) {
13224         this.fireEvent('evententer', this, el, event);
13225     },
13226     
13227     onEventLeave: function (e, el,event,d) {
13228         this.fireEvent('eventleave', this, el, event);
13229     },
13230     
13231     onEventClick: function (e, el,event,d) {
13232         this.fireEvent('eventclick', this, el, event);
13233     },
13234     
13235     onMonthChange: function () {
13236         this.store.load();
13237     },
13238     
13239     onMoreEventClick: function(e, el, more)
13240     {
13241         var _this = this;
13242         
13243         this.calpopover.placement = 'right';
13244         this.calpopover.setTitle('More');
13245         
13246         this.calpopover.setContent('');
13247         
13248         var ctr = this.calpopover.el.select('.popover-content', true).first();
13249         
13250         Roo.each(more, function(m){
13251             var cfg = {
13252                 cls : 'fc-event-hori fc-event-draggable',
13253                 html : m.title
13254             }
13255             var cg = ctr.createChild(cfg);
13256             
13257             cg.on('click', _this.onEventClick, _this, m);
13258         });
13259         
13260         this.calpopover.show(el);
13261         
13262         
13263     },
13264     
13265     onLoad: function () 
13266     {   
13267         this.calevents = [];
13268         var cal = this;
13269         
13270         if(this.store.getCount() > 0){
13271             this.store.data.each(function(d){
13272                cal.addItem({
13273                     id : d.data.id,
13274                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
13275                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
13276                     time : d.data.start_time,
13277                     title : d.data.title,
13278                     description : d.data.description,
13279                     venue : d.data.venue
13280                 });
13281             });
13282         }
13283         
13284         this.renderEvents();
13285         
13286         if(this.calevents.length && this.loadMask){
13287             this.maskEl.hide();
13288         }
13289     },
13290     
13291     onBeforeLoad: function()
13292     {
13293         this.clearEvents();
13294         if(this.loadMask){
13295             this.maskEl.show();
13296         }
13297     }
13298 });
13299
13300  
13301  /*
13302  * - LGPL
13303  *
13304  * element
13305  * 
13306  */
13307
13308 /**
13309  * @class Roo.bootstrap.Popover
13310  * @extends Roo.bootstrap.Component
13311  * Bootstrap Popover class
13312  * @cfg {String} html contents of the popover   (or false to use children..)
13313  * @cfg {String} title of popover (or false to hide)
13314  * @cfg {String} placement how it is placed
13315  * @cfg {String} trigger click || hover (or false to trigger manually)
13316  * @cfg {String} over what (parent or false to trigger manually.)
13317  * 
13318  * @constructor
13319  * Create a new Popover
13320  * @param {Object} config The config object
13321  */
13322
13323 Roo.bootstrap.Popover = function(config){
13324     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
13325 };
13326
13327 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
13328     
13329     title: 'Fill in a title',
13330     html: false,
13331     
13332     placement : 'right',
13333     trigger : 'hover', // hover
13334     
13335     over: 'parent',
13336     
13337     can_build_overlaid : false,
13338     
13339     getChildContainer : function()
13340     {
13341         return this.el.select('.popover-content',true).first();
13342     },
13343     
13344     getAutoCreate : function(){
13345          Roo.log('make popover?');
13346         var cfg = {
13347            cls : 'popover roo-dynamic',
13348            style: 'display:block',
13349            cn : [
13350                 {
13351                     cls : 'arrow'
13352                 },
13353                 {
13354                     cls : 'popover-inner',
13355                     cn : [
13356                         {
13357                             tag: 'h3',
13358                             cls: 'popover-title',
13359                             html : this.title
13360                         },
13361                         {
13362                             cls : 'popover-content',
13363                             html : this.html
13364                         }
13365                     ]
13366                     
13367                 }
13368            ]
13369         };
13370         
13371         return cfg;
13372     },
13373     setTitle: function(str)
13374     {
13375         this.el.select('.popover-title',true).first().dom.innerHTML = str;
13376     },
13377     setContent: function(str)
13378     {
13379         this.el.select('.popover-content',true).first().dom.innerHTML = str;
13380     },
13381     // as it get's added to the bottom of the page.
13382     onRender : function(ct, position)
13383     {
13384         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
13385         if(!this.el){
13386             var cfg = Roo.apply({},  this.getAutoCreate());
13387             cfg.id = Roo.id();
13388             
13389             if (this.cls) {
13390                 cfg.cls += ' ' + this.cls;
13391             }
13392             if (this.style) {
13393                 cfg.style = this.style;
13394             }
13395             Roo.log("adding to ")
13396             this.el = Roo.get(document.body).createChild(cfg, position);
13397             Roo.log(this.el);
13398         }
13399         this.initEvents();
13400     },
13401     
13402     initEvents : function()
13403     {
13404         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
13405         this.el.enableDisplayMode('block');
13406         this.el.hide();
13407         if (this.over === false) {
13408             return; 
13409         }
13410         if (this.triggers === false) {
13411             return;
13412         }
13413         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13414         var triggers = this.trigger ? this.trigger.split(' ') : [];
13415         Roo.each(triggers, function(trigger) {
13416         
13417             if (trigger == 'click') {
13418                 on_el.on('click', this.toggle, this);
13419             } else if (trigger != 'manual') {
13420                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
13421                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
13422       
13423                 on_el.on(eventIn  ,this.enter, this);
13424                 on_el.on(eventOut, this.leave, this);
13425             }
13426         }, this);
13427         
13428     },
13429     
13430     
13431     // private
13432     timeout : null,
13433     hoverState : null,
13434     
13435     toggle : function () {
13436         this.hoverState == 'in' ? this.leave() : this.enter();
13437     },
13438     
13439     enter : function () {
13440        
13441     
13442         clearTimeout(this.timeout);
13443     
13444         this.hoverState = 'in'
13445     
13446         if (!this.delay || !this.delay.show) {
13447             this.show();
13448             return 
13449         }
13450         var _t = this;
13451         this.timeout = setTimeout(function () {
13452             if (_t.hoverState == 'in') {
13453                 _t.show();
13454             }
13455         }, this.delay.show)
13456     },
13457     leave : function() {
13458         clearTimeout(this.timeout);
13459     
13460         this.hoverState = 'out'
13461     
13462         if (!this.delay || !this.delay.hide) {
13463             this.hide();
13464             return 
13465         }
13466         var _t = this;
13467         this.timeout = setTimeout(function () {
13468             if (_t.hoverState == 'out') {
13469                 _t.hide();
13470             }
13471         }, this.delay.hide)
13472     },
13473     
13474     show : function (on_el)
13475     {
13476         if (!on_el) {
13477             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13478         }
13479         // set content.
13480         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
13481         if (this.html !== false) {
13482             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
13483         }
13484         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
13485         if (!this.title.length) {
13486             this.el.select('.popover-title',true).hide();
13487         }
13488         
13489         var placement = typeof this.placement == 'function' ?
13490             this.placement.call(this, this.el, on_el) :
13491             this.placement;
13492             
13493         var autoToken = /\s?auto?\s?/i;
13494         var autoPlace = autoToken.test(placement);
13495         if (autoPlace) {
13496             placement = placement.replace(autoToken, '') || 'top';
13497         }
13498         
13499         //this.el.detach()
13500         //this.el.setXY([0,0]);
13501         this.el.show();
13502         this.el.dom.style.display='block';
13503         this.el.addClass(placement);
13504         
13505         //this.el.appendTo(on_el);
13506         
13507         var p = this.getPosition();
13508         var box = this.el.getBox();
13509         
13510         if (autoPlace) {
13511             // fixme..
13512         }
13513         var align = Roo.bootstrap.Popover.alignment[placement]
13514         this.el.alignTo(on_el, align[0],align[1]);
13515         //var arrow = this.el.select('.arrow',true).first();
13516         //arrow.set(align[2], 
13517         
13518         this.el.addClass('in');
13519         this.hoverState = null;
13520         
13521         if (this.el.hasClass('fade')) {
13522             // fade it?
13523         }
13524         
13525     },
13526     hide : function()
13527     {
13528         this.el.setXY([0,0]);
13529         this.el.removeClass('in');
13530         this.el.hide();
13531         
13532     }
13533     
13534 });
13535
13536 Roo.bootstrap.Popover.alignment = {
13537     'left' : ['r-l', [-10,0], 'right'],
13538     'right' : ['l-r', [10,0], 'left'],
13539     'bottom' : ['t-b', [0,10], 'top'],
13540     'top' : [ 'b-t', [0,-10], 'bottom']
13541 };
13542
13543  /*
13544  * - LGPL
13545  *
13546  * Progress
13547  * 
13548  */
13549
13550 /**
13551  * @class Roo.bootstrap.Progress
13552  * @extends Roo.bootstrap.Component
13553  * Bootstrap Progress class
13554  * @cfg {Boolean} striped striped of the progress bar
13555  * @cfg {Boolean} active animated of the progress bar
13556  * 
13557  * 
13558  * @constructor
13559  * Create a new Progress
13560  * @param {Object} config The config object
13561  */
13562
13563 Roo.bootstrap.Progress = function(config){
13564     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
13565 };
13566
13567 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
13568     
13569     striped : false,
13570     active: false,
13571     
13572     getAutoCreate : function(){
13573         var cfg = {
13574             tag: 'div',
13575             cls: 'progress'
13576         };
13577         
13578         
13579         if(this.striped){
13580             cfg.cls += ' progress-striped';
13581         }
13582       
13583         if(this.active){
13584             cfg.cls += ' active';
13585         }
13586         
13587         
13588         return cfg;
13589     }
13590    
13591 });
13592
13593  
13594
13595  /*
13596  * - LGPL
13597  *
13598  * ProgressBar
13599  * 
13600  */
13601
13602 /**
13603  * @class Roo.bootstrap.ProgressBar
13604  * @extends Roo.bootstrap.Component
13605  * Bootstrap ProgressBar class
13606  * @cfg {Number} aria_valuenow aria-value now
13607  * @cfg {Number} aria_valuemin aria-value min
13608  * @cfg {Number} aria_valuemax aria-value max
13609  * @cfg {String} label label for the progress bar
13610  * @cfg {String} panel (success | info | warning | danger )
13611  * @cfg {String} role role of the progress bar
13612  * @cfg {String} sr_only text
13613  * 
13614  * 
13615  * @constructor
13616  * Create a new ProgressBar
13617  * @param {Object} config The config object
13618  */
13619
13620 Roo.bootstrap.ProgressBar = function(config){
13621     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
13622 };
13623
13624 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
13625     
13626     aria_valuenow : 0,
13627     aria_valuemin : 0,
13628     aria_valuemax : 100,
13629     label : false,
13630     panel : false,
13631     role : false,
13632     sr_only: false,
13633     
13634     getAutoCreate : function()
13635     {
13636         
13637         var cfg = {
13638             tag: 'div',
13639             cls: 'progress-bar',
13640             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
13641         };
13642         
13643         if(this.sr_only){
13644             cfg.cn = {
13645                 tag: 'span',
13646                 cls: 'sr-only',
13647                 html: this.sr_only
13648             }
13649         }
13650         
13651         if(this.role){
13652             cfg.role = this.role;
13653         }
13654         
13655         if(this.aria_valuenow){
13656             cfg['aria-valuenow'] = this.aria_valuenow;
13657         }
13658         
13659         if(this.aria_valuemin){
13660             cfg['aria-valuemin'] = this.aria_valuemin;
13661         }
13662         
13663         if(this.aria_valuemax){
13664             cfg['aria-valuemax'] = this.aria_valuemax;
13665         }
13666         
13667         if(this.label && !this.sr_only){
13668             cfg.html = this.label;
13669         }
13670         
13671         if(this.panel){
13672             cfg.cls += ' progress-bar-' + this.panel;
13673         }
13674         
13675         return cfg;
13676     },
13677     
13678     update : function(aria_valuenow)
13679     {
13680         this.aria_valuenow = aria_valuenow;
13681         
13682         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
13683     }
13684    
13685 });
13686
13687  
13688
13689  /*
13690  * - LGPL
13691  *
13692  * column
13693  * 
13694  */
13695
13696 /**
13697  * @class Roo.bootstrap.TabGroup
13698  * @extends Roo.bootstrap.Column
13699  * Bootstrap Column class
13700  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
13701  * @cfg {Boolean} carousel true to make the group behave like a carousel
13702  * 
13703  * @constructor
13704  * Create a new TabGroup
13705  * @param {Object} config The config object
13706  */
13707
13708 Roo.bootstrap.TabGroup = function(config){
13709     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
13710     if (!this.navId) {
13711         this.navId = Roo.id();
13712     }
13713     this.tabs = [];
13714     Roo.bootstrap.TabGroup.register(this);
13715     
13716 };
13717
13718 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
13719     
13720     carousel : false,
13721      
13722     getAutoCreate : function()
13723     {
13724         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
13725         
13726         cfg.cls += ' tab-content';
13727         
13728         if (this.carousel) {
13729             cfg.cls += ' carousel slide';
13730         }
13731         
13732         return cfg;
13733     },
13734     
13735     /**
13736     * register a Navigation item
13737     * @param {Roo.bootstrap.NavItem} the navitem to add
13738     */
13739     register : function(item)
13740     {
13741         this.tabs.push( item);
13742         item.navId = this.navId; // not really needed..
13743     
13744     },
13745     
13746     getActivePanel : function()
13747     {
13748         var r = false;
13749         Roo.each(this.tabs, function(t) {
13750             if (t.active) {
13751                 r = t;
13752                 return false;
13753             }
13754             return null;
13755         });
13756         return r;
13757         
13758     },
13759     getPanelByName : function(n)
13760     {
13761         var r = false;
13762         Roo.each(this.tabs, function(t) {
13763             if (t.tabId == n) {
13764                 r = t;
13765                 return false;
13766             }
13767             return null;
13768         });
13769         return r;
13770     },
13771     indexOfPanel : function(p)
13772     {
13773         var r = false;
13774         Roo.each(this.tabs, function(t,i) {
13775             if (t.tabId == p.tabId) {
13776                 r = i;
13777                 return false;
13778             }
13779             return null;
13780         });
13781         return r;
13782     },
13783     showPanel : function (pan)
13784     {
13785         if (typeof(pan) == 'pnumber') {
13786             pan = this.tabs[pan];
13787         }
13788         if (typeof(pan) == 'string') {
13789             pan = this.getPanelByName(pan);
13790         }
13791         if (pan.tabId == this.getActivePanel().tabId) {
13792             return;
13793         }
13794         this.getActivePanel().setActive(false);
13795         pan.setActive(true);
13796         
13797     },
13798     showNextPanel : function()
13799     {
13800         var i = this.indexOfPanel(this.getActivePanel());
13801         if (i > this.tabs.length) {
13802             return;
13803         }
13804         this.showPanel(this.tabs[i+1]);
13805     },
13806     showPrevPanel : function()
13807     {
13808         var i = this.indexOfPanel(this.getActivePanel());
13809         if (i  < 1) {
13810             return;
13811         }
13812         this.showPanel(this.tabs[i-1]);
13813     }
13814     
13815     
13816   
13817 });
13818
13819  
13820
13821  
13822  
13823 Roo.apply(Roo.bootstrap.TabGroup, {
13824     
13825     groups: {},
13826      /**
13827     * register a Navigation Group
13828     * @param {Roo.bootstrap.NavGroup} the navgroup to add
13829     */
13830     register : function(navgrp)
13831     {
13832         this.groups[navgrp.navId] = navgrp;
13833         
13834     },
13835     /**
13836     * fetch a Navigation Group based on the navigation ID
13837     * if one does not exist , it will get created.
13838     * @param {string} the navgroup to add
13839     * @returns {Roo.bootstrap.NavGroup} the navgroup 
13840     */
13841     get: function(navId) {
13842         if (typeof(this.groups[navId]) == 'undefined') {
13843             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
13844         }
13845         return this.groups[navId] ;
13846     }
13847     
13848     
13849     
13850 });
13851
13852  /*
13853  * - LGPL
13854  *
13855  * TabPanel
13856  * 
13857  */
13858
13859 /**
13860  * @class Roo.bootstrap.TabPanel
13861  * @extends Roo.bootstrap.Component
13862  * Bootstrap TabPanel class
13863  * @cfg {Boolean} active panel active
13864  * @cfg {String} html panel content
13865  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
13866  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
13867  * 
13868  * 
13869  * @constructor
13870  * Create a new TabPanel
13871  * @param {Object} config The config object
13872  */
13873
13874 Roo.bootstrap.TabPanel = function(config){
13875     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
13876      this.addEvents({
13877         /**
13878              * @event changed
13879              * Fires when the active status changes
13880              * @param {Roo.bootstrap.TabPanel} this
13881              * @param {Boolean} state the new state
13882             
13883          */
13884         'changed': true
13885      });
13886     this.tabId = this.tabId || Roo.id();
13887   
13888 };
13889
13890 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
13891     
13892     active: false,
13893     html: false,
13894     tabId: false,
13895     navId : false,
13896     
13897     getAutoCreate : function(){
13898         var cfg = {
13899             tag: 'div',
13900             cls: 'tab-pane',
13901             html: this.html || ''
13902         };
13903         
13904         if(this.active){
13905             cfg.cls += ' active';
13906         }
13907         
13908         if(this.tabId){
13909             cfg.tabId = this.tabId;
13910         }
13911         
13912         return cfg;
13913     },
13914     
13915     initEvents:  function()
13916     {
13917         Roo.log('-------- init events on tab panel ---------');
13918         
13919         var p = this.parent();
13920         this.navId = this.navId || p.navId;
13921         
13922         if (typeof(this.navId) != 'undefined') {
13923             // not really needed.. but just in case.. parent should be a NavGroup.
13924             var tg = Roo.bootstrap.TabGroup.get(this.navId);
13925              Roo.log(['register', tg, this]);
13926             tg.register(this);
13927         }
13928     },
13929     
13930     
13931     onRender : function(ct, position)
13932     {
13933        // Roo.log("Call onRender: " + this.xtype);
13934         
13935         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
13936         
13937         // registration with navgroups..
13938         if (this.navId && this.tabId) {
13939             var grp = Roo.bootstrap.NavGroup.get(this.navId);
13940             if (grp) {
13941                 //code
13942                 var item = grp.getNavItem(this.tabId);
13943                 if (!item) {
13944                     Roo.log("could not find navID:"  + this.navId + ", tabId: " + this.tabId);
13945                 } else {
13946                     item.on('changed', function(item, state) {
13947                         this.setActive(state);
13948                     }, this);
13949                 }
13950             }
13951         }
13952         
13953         
13954         
13955     },
13956     setActive: function(state)
13957     {
13958         Roo.log("panel - set active " + this.tabId + "=" + state);
13959         
13960         this.active = state;
13961         if (!state) {
13962             this.el.removeClass('active');
13963             
13964         } else  if (!this.el.hasClass('active')) {
13965             this.el.addClass('active');
13966         }
13967         this.fireEvent('changed', this, state);
13968     }
13969     
13970     
13971 });
13972  
13973
13974  
13975
13976  /*
13977  * - LGPL
13978  *
13979  * DateField
13980  * 
13981  */
13982
13983 /**
13984  * @class Roo.bootstrap.DateField
13985  * @extends Roo.bootstrap.Input
13986  * Bootstrap DateField class
13987  * @cfg {Number} weekStart default 0
13988  * @cfg {Number} weekStart default 0
13989  * @cfg {Number} viewMode default empty, (months|years)
13990  * @cfg {Number} minViewMode default empty, (months|years)
13991  * @cfg {Number} startDate default -Infinity
13992  * @cfg {Number} endDate default Infinity
13993  * @cfg {Boolean} todayHighlight default false
13994  * @cfg {Boolean} todayBtn default false
13995  * @cfg {Boolean} calendarWeeks default false
13996  * @cfg {Object} daysOfWeekDisabled default empty
13997  * 
13998  * @cfg {Boolean} keyboardNavigation default true
13999  * @cfg {String} language default en
14000  * 
14001  * @constructor
14002  * Create a new DateField
14003  * @param {Object} config The config object
14004  */
14005
14006 Roo.bootstrap.DateField = function(config){
14007     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
14008      this.addEvents({
14009             /**
14010              * @event show
14011              * Fires when this field show.
14012              * @param {Roo.bootstrap.DateField} this
14013              * @param {Mixed} date The date value
14014              */
14015             show : true,
14016             /**
14017              * @event show
14018              * Fires when this field hide.
14019              * @param {Roo.bootstrap.DateField} this
14020              * @param {Mixed} date The date value
14021              */
14022             hide : true,
14023             /**
14024              * @event select
14025              * Fires when select a date.
14026              * @param {Roo.bootstrap.DateField} this
14027              * @param {Mixed} date The date value
14028              */
14029             select : true
14030         });
14031 };
14032
14033 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
14034     
14035     /**
14036      * @cfg {String} format
14037      * The default date format string which can be overriden for localization support.  The format must be
14038      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
14039      */
14040     format : "m/d/y",
14041     /**
14042      * @cfg {String} altFormats
14043      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
14044      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
14045      */
14046     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
14047     
14048     weekStart : 0,
14049     
14050     viewMode : '',
14051     
14052     minViewMode : '',
14053     
14054     todayHighlight : false,
14055     
14056     todayBtn: false,
14057     
14058     language: 'en',
14059     
14060     keyboardNavigation: true,
14061     
14062     calendarWeeks: false,
14063     
14064     startDate: -Infinity,
14065     
14066     endDate: Infinity,
14067     
14068     daysOfWeekDisabled: [],
14069     
14070     _events: [],
14071     
14072     UTCDate: function()
14073     {
14074         return new Date(Date.UTC.apply(Date, arguments));
14075     },
14076     
14077     UTCToday: function()
14078     {
14079         var today = new Date();
14080         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
14081     },
14082     
14083     getDate: function() {
14084             var d = this.getUTCDate();
14085             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
14086     },
14087     
14088     getUTCDate: function() {
14089             return this.date;
14090     },
14091     
14092     setDate: function(d) {
14093             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
14094     },
14095     
14096     setUTCDate: function(d) {
14097             this.date = d;
14098             this.setValue(this.formatDate(this.date));
14099     },
14100         
14101     onRender: function(ct, position)
14102     {
14103         
14104         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
14105         
14106         this.language = this.language || 'en';
14107         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
14108         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
14109         
14110         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
14111         this.format = this.format || 'm/d/y';
14112         this.isInline = false;
14113         this.isInput = true;
14114         this.component = this.el.select('.add-on', true).first() || false;
14115         this.component = (this.component && this.component.length === 0) ? false : this.component;
14116         this.hasInput = this.component && this.inputEL().length;
14117         
14118         if (typeof(this.minViewMode === 'string')) {
14119             switch (this.minViewMode) {
14120                 case 'months':
14121                     this.minViewMode = 1;
14122                     break;
14123                 case 'years':
14124                     this.minViewMode = 2;
14125                     break;
14126                 default:
14127                     this.minViewMode = 0;
14128                     break;
14129             }
14130         }
14131         
14132         if (typeof(this.viewMode === 'string')) {
14133             switch (this.viewMode) {
14134                 case 'months':
14135                     this.viewMode = 1;
14136                     break;
14137                 case 'years':
14138                     this.viewMode = 2;
14139                     break;
14140                 default:
14141                     this.viewMode = 0;
14142                     break;
14143             }
14144         }
14145                 
14146         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
14147         
14148         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14149         
14150         this.picker().on('mousedown', this.onMousedown, this);
14151         this.picker().on('click', this.onClick, this);
14152         
14153         this.picker().addClass('datepicker-dropdown');
14154         
14155         this.startViewMode = this.viewMode;
14156         
14157         
14158         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
14159             if(!this.calendarWeeks){
14160                 v.remove();
14161                 return;
14162             };
14163             
14164             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
14165             v.attr('colspan', function(i, val){
14166                 return parseInt(val) + 1;
14167             });
14168         })
14169                         
14170         
14171         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
14172         
14173         this.setStartDate(this.startDate);
14174         this.setEndDate(this.endDate);
14175         
14176         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
14177         
14178         this.fillDow();
14179         this.fillMonths();
14180         this.update();
14181         this.showMode();
14182         
14183         if(this.isInline) {
14184             this.show();
14185         }
14186     },
14187     
14188     picker : function()
14189     {
14190         return this.el.select('.datepicker', true).first();
14191     },
14192     
14193     fillDow: function()
14194     {
14195         var dowCnt = this.weekStart;
14196         
14197         var dow = {
14198             tag: 'tr',
14199             cn: [
14200                 
14201             ]
14202         };
14203         
14204         if(this.calendarWeeks){
14205             dow.cn.push({
14206                 tag: 'th',
14207                 cls: 'cw',
14208                 html: '&nbsp;'
14209             })
14210         }
14211         
14212         while (dowCnt < this.weekStart + 7) {
14213             dow.cn.push({
14214                 tag: 'th',
14215                 cls: 'dow',
14216                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
14217             });
14218         }
14219         
14220         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
14221     },
14222     
14223     fillMonths: function()
14224     {    
14225         var i = 0
14226         var months = this.picker().select('>.datepicker-months td', true).first();
14227         
14228         months.dom.innerHTML = '';
14229         
14230         while (i < 12) {
14231             var month = {
14232                 tag: 'span',
14233                 cls: 'month',
14234                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
14235             }
14236             
14237             months.createChild(month);
14238         }
14239         
14240     },
14241     
14242     update: function()
14243     {
14244         
14245         this.date = (typeof(this.date) === 'undefined') ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
14246         
14247         if (this.date < this.startDate) {
14248             this.viewDate = new Date(this.startDate);
14249         } else if (this.date > this.endDate) {
14250             this.viewDate = new Date(this.endDate);
14251         } else {
14252             this.viewDate = new Date(this.date);
14253         }
14254         
14255         this.fill();
14256     },
14257     
14258     fill: function() 
14259     {
14260         var d = new Date(this.viewDate),
14261                 year = d.getUTCFullYear(),
14262                 month = d.getUTCMonth(),
14263                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
14264                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
14265                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
14266                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
14267                 currentDate = this.date && this.date.valueOf(),
14268                 today = this.UTCToday();
14269         
14270         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
14271         
14272 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
14273         
14274 //        this.picker.select('>tfoot th.today').
14275 //                                              .text(dates[this.language].today)
14276 //                                              .toggle(this.todayBtn !== false);
14277     
14278         this.updateNavArrows();
14279         this.fillMonths();
14280                                                 
14281         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
14282         
14283         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
14284          
14285         prevMonth.setUTCDate(day);
14286         
14287         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
14288         
14289         var nextMonth = new Date(prevMonth);
14290         
14291         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
14292         
14293         nextMonth = nextMonth.valueOf();
14294         
14295         var fillMonths = false;
14296         
14297         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
14298         
14299         while(prevMonth.valueOf() < nextMonth) {
14300             var clsName = '';
14301             
14302             if (prevMonth.getUTCDay() === this.weekStart) {
14303                 if(fillMonths){
14304                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
14305                 }
14306                     
14307                 fillMonths = {
14308                     tag: 'tr',
14309                     cn: []
14310                 };
14311                 
14312                 if(this.calendarWeeks){
14313                     // ISO 8601: First week contains first thursday.
14314                     // ISO also states week starts on Monday, but we can be more abstract here.
14315                     var
14316                     // Start of current week: based on weekstart/current date
14317                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
14318                     // Thursday of this week
14319                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
14320                     // First Thursday of year, year from thursday
14321                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
14322                     // Calendar week: ms between thursdays, div ms per day, div 7 days
14323                     calWeek =  (th - yth) / 864e5 / 7 + 1;
14324                     
14325                     fillMonths.cn.push({
14326                         tag: 'td',
14327                         cls: 'cw',
14328                         html: calWeek
14329                     });
14330                 }
14331             }
14332             
14333             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
14334                 clsName += ' old';
14335             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
14336                 clsName += ' new';
14337             }
14338             if (this.todayHighlight &&
14339                 prevMonth.getUTCFullYear() == today.getFullYear() &&
14340                 prevMonth.getUTCMonth() == today.getMonth() &&
14341                 prevMonth.getUTCDate() == today.getDate()) {
14342                 clsName += ' today';
14343             }
14344             
14345             if (currentDate && prevMonth.valueOf() === currentDate) {
14346                 clsName += ' active';
14347             }
14348             
14349             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
14350                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
14351                     clsName += ' disabled';
14352             }
14353             
14354             fillMonths.cn.push({
14355                 tag: 'td',
14356                 cls: 'day ' + clsName,
14357                 html: prevMonth.getDate()
14358             })
14359             
14360             prevMonth.setDate(prevMonth.getDate()+1);
14361         }
14362           
14363         var currentYear = this.date && this.date.getUTCFullYear();
14364         var currentMonth = this.date && this.date.getUTCMonth();
14365         
14366         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
14367         
14368         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
14369             v.removeClass('active');
14370             
14371             if(currentYear === year && k === currentMonth){
14372                 v.addClass('active');
14373             }
14374             
14375             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
14376                 v.addClass('disabled');
14377             }
14378             
14379         });
14380         
14381         
14382         year = parseInt(year/10, 10) * 10;
14383         
14384         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
14385         
14386         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
14387         
14388         year -= 1;
14389         for (var i = -1; i < 11; i++) {
14390             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
14391                 tag: 'span',
14392                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
14393                 html: year
14394             })
14395             
14396             year += 1;
14397         }
14398     },
14399     
14400     showMode: function(dir) 
14401     {
14402         if (dir) {
14403             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
14404         }
14405         Roo.each(this.picker().select('>div',true).elements, function(v){
14406             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14407             v.hide();
14408         });
14409         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
14410     },
14411     
14412     place: function()
14413     {
14414         if(this.isInline) return;
14415         
14416         this.picker().removeClass(['bottom', 'top']);
14417         
14418         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
14419             /*
14420              * place to the top of element!
14421              *
14422              */
14423             
14424             this.picker().addClass('top');
14425             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
14426             
14427             return;
14428         }
14429         
14430         this.picker().addClass('bottom');
14431         
14432         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
14433     },
14434     
14435     parseDate : function(value)
14436     {
14437         if(!value || value instanceof Date){
14438             return value;
14439         }
14440         var v = Date.parseDate(value, this.format);
14441         if (!v && this.useIso) {
14442             v = Date.parseDate(value, 'Y-m-d');
14443         }
14444         if(!v && this.altFormats){
14445             if(!this.altFormatsArray){
14446                 this.altFormatsArray = this.altFormats.split("|");
14447             }
14448             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
14449                 v = Date.parseDate(value, this.altFormatsArray[i]);
14450             }
14451         }
14452         return v;
14453     },
14454     
14455     formatDate : function(date, fmt)
14456     {
14457         return (!date || !(date instanceof Date)) ?
14458         date : date.dateFormat(fmt || this.format);
14459     },
14460     
14461     onFocus : function()
14462     {
14463         Roo.bootstrap.DateField.superclass.onFocus.call(this);
14464         this.show();
14465     },
14466     
14467     onBlur : function()
14468     {
14469         Roo.bootstrap.DateField.superclass.onBlur.call(this);
14470         
14471         var d = this.inputEl().getValue();
14472         
14473         if(d && d.length){
14474             this.setValue(d);
14475         }
14476                 
14477         this.hide();
14478     },
14479     
14480     show : function()
14481     {
14482         this.picker().show();
14483         this.update();
14484         this.place();
14485         
14486         this.fireEvent('show', this, this.date);
14487     },
14488     
14489     hide : function()
14490     {
14491         if(this.isInline) return;
14492         this.picker().hide();
14493         this.viewMode = this.startViewMode;
14494         this.showMode();
14495         
14496         this.fireEvent('hide', this, this.date);
14497         
14498     },
14499     
14500     onMousedown: function(e)
14501     {
14502         e.stopPropagation();
14503         e.preventDefault();
14504     },
14505     
14506     keyup: function(e)
14507     {
14508         Roo.bootstrap.DateField.superclass.keyup.call(this);
14509         this.update();
14510     },
14511
14512     setValue: function(v)
14513     {
14514         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
14515         
14516         var d = new Date(v);
14517         
14518         if(isNaN(d.getTime())){
14519             return;
14520         }
14521         
14522         this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
14523
14524         this.update();
14525
14526         this.fireEvent('select', this, this.date);
14527         
14528     },
14529     
14530     getValue: function()
14531     {
14532         return this.formatDate(this.date);
14533     },
14534     
14535     fireKey: function(e)
14536     {
14537         if (!this.picker().isVisible()){
14538             if (e.keyCode == 27) // allow escape to hide and re-show picker
14539                 this.show();
14540             return;
14541         }
14542         
14543         var dateChanged = false,
14544         dir, day, month,
14545         newDate, newViewDate;
14546         
14547         switch(e.keyCode){
14548             case 27: // escape
14549                 this.hide();
14550                 e.preventDefault();
14551                 break;
14552             case 37: // left
14553             case 39: // right
14554                 if (!this.keyboardNavigation) break;
14555                 dir = e.keyCode == 37 ? -1 : 1;
14556                 
14557                 if (e.ctrlKey){
14558                     newDate = this.moveYear(this.date, dir);
14559                     newViewDate = this.moveYear(this.viewDate, dir);
14560                 } else if (e.shiftKey){
14561                     newDate = this.moveMonth(this.date, dir);
14562                     newViewDate = this.moveMonth(this.viewDate, dir);
14563                 } else {
14564                     newDate = new Date(this.date);
14565                     newDate.setUTCDate(this.date.getUTCDate() + dir);
14566                     newViewDate = new Date(this.viewDate);
14567                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
14568                 }
14569                 if (this.dateWithinRange(newDate)){
14570                     this.date = newDate;
14571                     this.viewDate = newViewDate;
14572                     this.setValue(this.formatDate(this.date));
14573 //                    this.update();
14574                     e.preventDefault();
14575                     dateChanged = true;
14576                 }
14577                 break;
14578             case 38: // up
14579             case 40: // down
14580                 if (!this.keyboardNavigation) break;
14581                 dir = e.keyCode == 38 ? -1 : 1;
14582                 if (e.ctrlKey){
14583                     newDate = this.moveYear(this.date, dir);
14584                     newViewDate = this.moveYear(this.viewDate, dir);
14585                 } else if (e.shiftKey){
14586                     newDate = this.moveMonth(this.date, dir);
14587                     newViewDate = this.moveMonth(this.viewDate, dir);
14588                 } else {
14589                     newDate = new Date(this.date);
14590                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
14591                     newViewDate = new Date(this.viewDate);
14592                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
14593                 }
14594                 if (this.dateWithinRange(newDate)){
14595                     this.date = newDate;
14596                     this.viewDate = newViewDate;
14597                     this.setValue(this.formatDate(this.date));
14598 //                    this.update();
14599                     e.preventDefault();
14600                     dateChanged = true;
14601                 }
14602                 break;
14603             case 13: // enter
14604                 this.setValue(this.formatDate(this.date));
14605                 this.hide();
14606                 e.preventDefault();
14607                 break;
14608             case 9: // tab
14609                 this.setValue(this.formatDate(this.date));
14610                 this.hide();
14611                 break;
14612             case 16: // shift
14613             case 17: // ctrl
14614             case 18: // alt
14615                 break;
14616             default :
14617                 this.hide();
14618                 
14619         }
14620     },
14621     
14622     
14623     onClick: function(e) 
14624     {
14625         e.stopPropagation();
14626         e.preventDefault();
14627         
14628         var target = e.getTarget();
14629         
14630         if(target.nodeName.toLowerCase() === 'i'){
14631             target = Roo.get(target).dom.parentNode;
14632         }
14633         
14634         var nodeName = target.nodeName;
14635         var className = target.className;
14636         var html = target.innerHTML;
14637         
14638         switch(nodeName.toLowerCase()) {
14639             case 'th':
14640                 switch(className) {
14641                     case 'switch':
14642                         this.showMode(1);
14643                         break;
14644                     case 'prev':
14645                     case 'next':
14646                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
14647                         switch(this.viewMode){
14648                                 case 0:
14649                                         this.viewDate = this.moveMonth(this.viewDate, dir);
14650                                         break;
14651                                 case 1:
14652                                 case 2:
14653                                         this.viewDate = this.moveYear(this.viewDate, dir);
14654                                         break;
14655                         }
14656                         this.fill();
14657                         break;
14658                     case 'today':
14659                         var date = new Date();
14660                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
14661 //                        this.fill()
14662                         this.setValue(this.formatDate(this.date));
14663                         
14664                         this.hide();
14665                         break;
14666                 }
14667                 break;
14668             case 'span':
14669                 if (className.indexOf('disabled') === -1) {
14670                     this.viewDate.setUTCDate(1);
14671                     if (className.indexOf('month') !== -1) {
14672                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
14673                     } else {
14674                         var year = parseInt(html, 10) || 0;
14675                         this.viewDate.setUTCFullYear(year);
14676                         
14677                     }
14678                     this.showMode(-1);
14679                     this.fill();
14680                 }
14681                 break;
14682                 
14683             case 'td':
14684                 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
14685                     var day = parseInt(html, 10) || 1;
14686                     var year = this.viewDate.getUTCFullYear(),
14687                         month = this.viewDate.getUTCMonth();
14688
14689                     if (className.indexOf('old') !== -1) {
14690                         if(month === 0 ){
14691                             month = 11;
14692                             year -= 1;
14693                         }else{
14694                             month -= 1;
14695                         }
14696                     } else if (className.indexOf('new') !== -1) {
14697                         if (month == 11) {
14698                             month = 0;
14699                             year += 1;
14700                         } else {
14701                             month += 1;
14702                         }
14703                     }
14704                     this.date = this.UTCDate(year, month, day,0,0,0,0);
14705                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
14706 //                    this.fill();
14707                     this.setValue(this.formatDate(this.date));
14708                     this.hide();
14709                 }
14710                 break;
14711         }
14712     },
14713     
14714     setStartDate: function(startDate)
14715     {
14716         this.startDate = startDate || -Infinity;
14717         if (this.startDate !== -Infinity) {
14718             this.startDate = this.parseDate(this.startDate);
14719         }
14720         this.update();
14721         this.updateNavArrows();
14722     },
14723
14724     setEndDate: function(endDate)
14725     {
14726         this.endDate = endDate || Infinity;
14727         if (this.endDate !== Infinity) {
14728             this.endDate = this.parseDate(this.endDate);
14729         }
14730         this.update();
14731         this.updateNavArrows();
14732     },
14733     
14734     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
14735     {
14736         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
14737         if (typeof(this.daysOfWeekDisabled) !== 'object') {
14738             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
14739         }
14740         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
14741             return parseInt(d, 10);
14742         });
14743         this.update();
14744         this.updateNavArrows();
14745     },
14746     
14747     updateNavArrows: function() 
14748     {
14749         var d = new Date(this.viewDate),
14750         year = d.getUTCFullYear(),
14751         month = d.getUTCMonth();
14752         
14753         Roo.each(this.picker().select('.prev', true).elements, function(v){
14754             v.show();
14755             switch (this.viewMode) {
14756                 case 0:
14757
14758                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
14759                         v.hide();
14760                     }
14761                     break;
14762                 case 1:
14763                 case 2:
14764                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
14765                         v.hide();
14766                     }
14767                     break;
14768             }
14769         });
14770         
14771         Roo.each(this.picker().select('.next', true).elements, function(v){
14772             v.show();
14773             switch (this.viewMode) {
14774                 case 0:
14775
14776                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
14777                         v.hide();
14778                     }
14779                     break;
14780                 case 1:
14781                 case 2:
14782                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
14783                         v.hide();
14784                     }
14785                     break;
14786             }
14787         })
14788     },
14789     
14790     moveMonth: function(date, dir)
14791     {
14792         if (!dir) return date;
14793         var new_date = new Date(date.valueOf()),
14794         day = new_date.getUTCDate(),
14795         month = new_date.getUTCMonth(),
14796         mag = Math.abs(dir),
14797         new_month, test;
14798         dir = dir > 0 ? 1 : -1;
14799         if (mag == 1){
14800             test = dir == -1
14801             // If going back one month, make sure month is not current month
14802             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
14803             ? function(){
14804                 return new_date.getUTCMonth() == month;
14805             }
14806             // If going forward one month, make sure month is as expected
14807             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
14808             : function(){
14809                 return new_date.getUTCMonth() != new_month;
14810             };
14811             new_month = month + dir;
14812             new_date.setUTCMonth(new_month);
14813             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
14814             if (new_month < 0 || new_month > 11)
14815                 new_month = (new_month + 12) % 12;
14816         } else {
14817             // For magnitudes >1, move one month at a time...
14818             for (var i=0; i<mag; i++)
14819                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
14820                 new_date = this.moveMonth(new_date, dir);
14821             // ...then reset the day, keeping it in the new month
14822             new_month = new_date.getUTCMonth();
14823             new_date.setUTCDate(day);
14824             test = function(){
14825                 return new_month != new_date.getUTCMonth();
14826             };
14827         }
14828         // Common date-resetting loop -- if date is beyond end of month, make it
14829         // end of month
14830         while (test()){
14831             new_date.setUTCDate(--day);
14832             new_date.setUTCMonth(new_month);
14833         }
14834         return new_date;
14835     },
14836
14837     moveYear: function(date, dir)
14838     {
14839         return this.moveMonth(date, dir*12);
14840     },
14841
14842     dateWithinRange: function(date)
14843     {
14844         return date >= this.startDate && date <= this.endDate;
14845     },
14846
14847     
14848     remove: function() 
14849     {
14850         this.picker().remove();
14851     }
14852    
14853 });
14854
14855 Roo.apply(Roo.bootstrap.DateField,  {
14856     
14857     head : {
14858         tag: 'thead',
14859         cn: [
14860         {
14861             tag: 'tr',
14862             cn: [
14863             {
14864                 tag: 'th',
14865                 cls: 'prev',
14866                 html: '<i class="fa fa-arrow-left"/>'
14867             },
14868             {
14869                 tag: 'th',
14870                 cls: 'switch',
14871                 colspan: '5'
14872             },
14873             {
14874                 tag: 'th',
14875                 cls: 'next',
14876                 html: '<i class="fa fa-arrow-right"/>'
14877             }
14878
14879             ]
14880         }
14881         ]
14882     },
14883     
14884     content : {
14885         tag: 'tbody',
14886         cn: [
14887         {
14888             tag: 'tr',
14889             cn: [
14890             {
14891                 tag: 'td',
14892                 colspan: '7'
14893             }
14894             ]
14895         }
14896         ]
14897     },
14898     
14899     footer : {
14900         tag: 'tfoot',
14901         cn: [
14902         {
14903             tag: 'tr',
14904             cn: [
14905             {
14906                 tag: 'th',
14907                 colspan: '7',
14908                 cls: 'today'
14909             }
14910                     
14911             ]
14912         }
14913         ]
14914     },
14915     
14916     dates:{
14917         en: {
14918             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
14919             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
14920             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
14921             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
14922             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
14923             today: "Today"
14924         }
14925     },
14926     
14927     modes: [
14928     {
14929         clsName: 'days',
14930         navFnc: 'Month',
14931         navStep: 1
14932     },
14933     {
14934         clsName: 'months',
14935         navFnc: 'FullYear',
14936         navStep: 1
14937     },
14938     {
14939         clsName: 'years',
14940         navFnc: 'FullYear',
14941         navStep: 10
14942     }]
14943 });
14944
14945 Roo.apply(Roo.bootstrap.DateField,  {
14946   
14947     template : {
14948         tag: 'div',
14949         cls: 'datepicker dropdown-menu',
14950         cn: [
14951         {
14952             tag: 'div',
14953             cls: 'datepicker-days',
14954             cn: [
14955             {
14956                 tag: 'table',
14957                 cls: 'table-condensed',
14958                 cn:[
14959                 Roo.bootstrap.DateField.head,
14960                 {
14961                     tag: 'tbody'
14962                 },
14963                 Roo.bootstrap.DateField.footer
14964                 ]
14965             }
14966             ]
14967         },
14968         {
14969             tag: 'div',
14970             cls: 'datepicker-months',
14971             cn: [
14972             {
14973                 tag: 'table',
14974                 cls: 'table-condensed',
14975                 cn:[
14976                 Roo.bootstrap.DateField.head,
14977                 Roo.bootstrap.DateField.content,
14978                 Roo.bootstrap.DateField.footer
14979                 ]
14980             }
14981             ]
14982         },
14983         {
14984             tag: 'div',
14985             cls: 'datepicker-years',
14986             cn: [
14987             {
14988                 tag: 'table',
14989                 cls: 'table-condensed',
14990                 cn:[
14991                 Roo.bootstrap.DateField.head,
14992                 Roo.bootstrap.DateField.content,
14993                 Roo.bootstrap.DateField.footer
14994                 ]
14995             }
14996             ]
14997         }
14998         ]
14999     }
15000 });
15001
15002  
15003
15004  /*
15005  * - LGPL
15006  *
15007  * TimeField
15008  * 
15009  */
15010
15011 /**
15012  * @class Roo.bootstrap.TimeField
15013  * @extends Roo.bootstrap.Input
15014  * Bootstrap DateField class
15015  * 
15016  * 
15017  * @constructor
15018  * Create a new TimeField
15019  * @param {Object} config The config object
15020  */
15021
15022 Roo.bootstrap.TimeField = function(config){
15023     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
15024     this.addEvents({
15025             /**
15026              * @event show
15027              * Fires when this field show.
15028              * @param {Roo.bootstrap.DateField} this
15029              * @param {Mixed} date The date value
15030              */
15031             show : true,
15032             /**
15033              * @event show
15034              * Fires when this field hide.
15035              * @param {Roo.bootstrap.DateField} this
15036              * @param {Mixed} date The date value
15037              */
15038             hide : true,
15039             /**
15040              * @event select
15041              * Fires when select a date.
15042              * @param {Roo.bootstrap.DateField} this
15043              * @param {Mixed} date The date value
15044              */
15045             select : true
15046         });
15047 };
15048
15049 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
15050     
15051     /**
15052      * @cfg {String} format
15053      * The default time format string which can be overriden for localization support.  The format must be
15054      * valid according to {@link Date#parseDate} (defaults to 'H:i').
15055      */
15056     format : "H:i",
15057        
15058     onRender: function(ct, position)
15059     {
15060         
15061         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
15062                 
15063         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
15064         
15065         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15066         
15067         this.pop = this.picker().select('>.datepicker-time',true).first();
15068         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block' 
15069         
15070         this.picker().on('mousedown', this.onMousedown, this);
15071         this.picker().on('click', this.onClick, this);
15072         
15073         this.picker().addClass('datepicker-dropdown');
15074     
15075         this.fillTime();
15076         this.update();
15077             
15078         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
15079         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
15080         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
15081         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
15082         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
15083         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
15084
15085     },
15086     
15087     fireKey: function(e){
15088         if (!this.picker().isVisible()){
15089             if (e.keyCode == 27) // allow escape to hide and re-show picker
15090                 this.show();
15091             return;
15092         }
15093
15094         e.preventDefault();
15095         
15096         switch(e.keyCode){
15097             case 27: // escape
15098                 this.hide();
15099                 break;
15100             case 37: // left
15101             case 39: // right
15102                 this.onTogglePeriod();
15103                 break;
15104             case 38: // up
15105                 this.onIncrementMinutes();
15106                 break;
15107             case 40: // down
15108                 this.onDecrementMinutes();
15109                 break;
15110             case 13: // enter
15111             case 9: // tab
15112                 this.setTime();
15113                 break;
15114         }
15115     },
15116     
15117     onClick: function(e) {
15118         e.stopPropagation();
15119         e.preventDefault();
15120     },
15121     
15122     picker : function()
15123     {
15124         return this.el.select('.datepicker', true).first();
15125     },
15126     
15127     fillTime: function()
15128     {    
15129         var time = this.pop.select('tbody', true).first();
15130         
15131         time.dom.innerHTML = '';
15132         
15133         time.createChild({
15134             tag: 'tr',
15135             cn: [
15136                 {
15137                     tag: 'td',
15138                     cn: [
15139                         {
15140                             tag: 'a',
15141                             href: '#',
15142                             cls: 'btn',
15143                             cn: [
15144                                 {
15145                                     tag: 'span',
15146                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
15147                                 }
15148                             ]
15149                         } 
15150                     ]
15151                 },
15152                 {
15153                     tag: 'td',
15154                     cls: 'separator'
15155                 },
15156                 {
15157                     tag: 'td',
15158                     cn: [
15159                         {
15160                             tag: 'a',
15161                             href: '#',
15162                             cls: 'btn',
15163                             cn: [
15164                                 {
15165                                     tag: 'span',
15166                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
15167                                 }
15168                             ]
15169                         }
15170                     ]
15171                 },
15172                 {
15173                     tag: 'td',
15174                     cls: 'separator'
15175                 }
15176             ]
15177         });
15178         
15179         time.createChild({
15180             tag: 'tr',
15181             cn: [
15182                 {
15183                     tag: 'td',
15184                     cn: [
15185                         {
15186                             tag: 'span',
15187                             cls: 'timepicker-hour',
15188                             html: '00'
15189                         }  
15190                     ]
15191                 },
15192                 {
15193                     tag: 'td',
15194                     cls: 'separator',
15195                     html: ':'
15196                 },
15197                 {
15198                     tag: 'td',
15199                     cn: [
15200                         {
15201                             tag: 'span',
15202                             cls: 'timepicker-minute',
15203                             html: '00'
15204                         }  
15205                     ]
15206                 },
15207                 {
15208                     tag: 'td',
15209                     cls: 'separator'
15210                 },
15211                 {
15212                     tag: 'td',
15213                     cn: [
15214                         {
15215                             tag: 'button',
15216                             type: 'button',
15217                             cls: 'btn btn-primary period',
15218                             html: 'AM'
15219                             
15220                         }
15221                     ]
15222                 }
15223             ]
15224         });
15225         
15226         time.createChild({
15227             tag: 'tr',
15228             cn: [
15229                 {
15230                     tag: 'td',
15231                     cn: [
15232                         {
15233                             tag: 'a',
15234                             href: '#',
15235                             cls: 'btn',
15236                             cn: [
15237                                 {
15238                                     tag: 'span',
15239                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
15240                                 }
15241                             ]
15242                         }
15243                     ]
15244                 },
15245                 {
15246                     tag: 'td',
15247                     cls: 'separator'
15248                 },
15249                 {
15250                     tag: 'td',
15251                     cn: [
15252                         {
15253                             tag: 'a',
15254                             href: '#',
15255                             cls: 'btn',
15256                             cn: [
15257                                 {
15258                                     tag: 'span',
15259                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
15260                                 }
15261                             ]
15262                         }
15263                     ]
15264                 },
15265                 {
15266                     tag: 'td',
15267                     cls: 'separator'
15268                 }
15269             ]
15270         });
15271         
15272     },
15273     
15274     update: function()
15275     {
15276         
15277         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
15278         
15279         this.fill();
15280     },
15281     
15282     fill: function() 
15283     {
15284         var hours = this.time.getHours();
15285         var minutes = this.time.getMinutes();
15286         var period = 'AM';
15287         
15288         if(hours > 11){
15289             period = 'PM';
15290         }
15291         
15292         if(hours == 0){
15293             hours = 12;
15294         }
15295         
15296         
15297         if(hours > 12){
15298             hours = hours - 12;
15299         }
15300         
15301         if(hours < 10){
15302             hours = '0' + hours;
15303         }
15304         
15305         if(minutes < 10){
15306             minutes = '0' + minutes;
15307         }
15308         
15309         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
15310         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
15311         this.pop.select('button', true).first().dom.innerHTML = period;
15312         
15313     },
15314     
15315     place: function()
15316     {   
15317         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
15318         
15319         var cls = ['bottom'];
15320         
15321         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
15322             cls.pop();
15323             cls.push('top');
15324         }
15325         
15326         cls.push('right');
15327         
15328         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
15329             cls.pop();
15330             cls.push('left');
15331         }
15332         
15333         this.picker().addClass(cls.join('-'));
15334         
15335         var _this = this;
15336         
15337         Roo.each(cls, function(c){
15338             if(c == 'bottom'){
15339                 _this.picker().setTop(_this.inputEl().getHeight());
15340                 return;
15341             }
15342             if(c == 'top'){
15343                 _this.picker().setTop(0 - _this.picker().getHeight());
15344                 return;
15345             }
15346             
15347             if(c == 'left'){
15348                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
15349                 return;
15350             }
15351             if(c == 'right'){
15352                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
15353                 return;
15354             }
15355         });
15356         
15357     },
15358   
15359     onFocus : function()
15360     {
15361         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
15362         this.show();
15363     },
15364     
15365     onBlur : function()
15366     {
15367         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
15368         this.hide();
15369     },
15370     
15371     show : function()
15372     {
15373         this.picker().show();
15374         this.pop.show();
15375         this.update();
15376         this.place();
15377         
15378         this.fireEvent('show', this, this.date);
15379     },
15380     
15381     hide : function()
15382     {
15383         this.picker().hide();
15384         this.pop.hide();
15385         
15386         this.fireEvent('hide', this, this.date);
15387     },
15388     
15389     setTime : function()
15390     {
15391         this.hide();
15392         this.setValue(this.time.format(this.format));
15393         
15394         this.fireEvent('select', this, this.date);
15395         
15396         
15397     },
15398     
15399     onMousedown: function(e){
15400         e.stopPropagation();
15401         e.preventDefault();
15402     },
15403     
15404     onIncrementHours: function()
15405     {
15406         Roo.log('onIncrementHours');
15407         this.time = this.time.add(Date.HOUR, 1);
15408         this.update();
15409         
15410     },
15411     
15412     onDecrementHours: function()
15413     {
15414         Roo.log('onDecrementHours');
15415         this.time = this.time.add(Date.HOUR, -1);
15416         this.update();
15417     },
15418     
15419     onIncrementMinutes: function()
15420     {
15421         Roo.log('onIncrementMinutes');
15422         this.time = this.time.add(Date.MINUTE, 1);
15423         this.update();
15424     },
15425     
15426     onDecrementMinutes: function()
15427     {
15428         Roo.log('onDecrementMinutes');
15429         this.time = this.time.add(Date.MINUTE, -1);
15430         this.update();
15431     },
15432     
15433     onTogglePeriod: function()
15434     {
15435         Roo.log('onTogglePeriod');
15436         this.time = this.time.add(Date.HOUR, 12);
15437         this.update();
15438     }
15439     
15440    
15441 });
15442
15443 Roo.apply(Roo.bootstrap.TimeField,  {
15444     
15445     content : {
15446         tag: 'tbody',
15447         cn: [
15448             {
15449                 tag: 'tr',
15450                 cn: [
15451                 {
15452                     tag: 'td',
15453                     colspan: '7'
15454                 }
15455                 ]
15456             }
15457         ]
15458     },
15459     
15460     footer : {
15461         tag: 'tfoot',
15462         cn: [
15463             {
15464                 tag: 'tr',
15465                 cn: [
15466                 {
15467                     tag: 'th',
15468                     colspan: '7',
15469                     cls: '',
15470                     cn: [
15471                         {
15472                             tag: 'button',
15473                             cls: 'btn btn-info ok',
15474                             html: 'OK'
15475                         }
15476                     ]
15477                 }
15478
15479                 ]
15480             }
15481         ]
15482     }
15483 });
15484
15485 Roo.apply(Roo.bootstrap.TimeField,  {
15486   
15487     template : {
15488         tag: 'div',
15489         cls: 'datepicker dropdown-menu',
15490         cn: [
15491             {
15492                 tag: 'div',
15493                 cls: 'datepicker-time',
15494                 cn: [
15495                 {
15496                     tag: 'table',
15497                     cls: 'table-condensed',
15498                     cn:[
15499                     Roo.bootstrap.TimeField.content,
15500                     Roo.bootstrap.TimeField.footer
15501                     ]
15502                 }
15503                 ]
15504             }
15505         ]
15506     }
15507 });
15508
15509  
15510
15511  /*
15512  * - LGPL
15513  *
15514  * CheckBox
15515  * 
15516  */
15517
15518 /**
15519  * @class Roo.bootstrap.CheckBox
15520  * @extends Roo.bootstrap.Input
15521  * Bootstrap CheckBox class
15522  * 
15523  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
15524  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
15525  * @cfg {String} boxLabel The text that appears beside the checkbox
15526  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
15527  * @cfg {Boolean} checked initnal the element
15528  * 
15529  * 
15530  * @constructor
15531  * Create a new CheckBox
15532  * @param {Object} config The config object
15533  */
15534
15535 Roo.bootstrap.CheckBox = function(config){
15536     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
15537    
15538         this.addEvents({
15539             /**
15540             * @event check
15541             * Fires when the element is checked or unchecked.
15542             * @param {Roo.bootstrap.CheckBox} this This input
15543             * @param {Boolean} checked The new checked value
15544             */
15545            check : true
15546         });
15547 };
15548
15549 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
15550     
15551     inputType: 'checkbox',
15552     inputValue: 1,
15553     valueOff: 0,
15554     boxLabel: false,
15555     checked: false,
15556     weight : false,
15557     
15558     getAutoCreate : function()
15559     {
15560         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
15561         
15562         var id = Roo.id();
15563         
15564         var cfg = {};
15565         
15566         cfg.cls = 'form-group checkbox' //input-group
15567         
15568         
15569         
15570         
15571         var input =  {
15572             tag: 'input',
15573             id : id,
15574             type : this.inputType,
15575             value : (!this.checked) ? this.valueOff : this.inputValue,
15576             cls : 'roo-checkbox', //'form-box',
15577             placeholder : this.placeholder || ''
15578             
15579         };
15580         
15581         if (this.weight) { // Validity check?
15582             cfg.cls += " checkbox-" + this.weight;
15583         }
15584         
15585         if (this.disabled) {
15586             input.disabled=true;
15587         }
15588         
15589         if(this.checked){
15590             input.checked = this.checked;
15591         }
15592         
15593         if (this.name) {
15594             input.name = this.name;
15595         }
15596         
15597         if (this.size) {
15598             input.cls += ' input-' + this.size;
15599         }
15600         
15601         var settings=this;
15602         ['xs','sm','md','lg'].map(function(size){
15603             if (settings[size]) {
15604                 cfg.cls += ' col-' + size + '-' + settings[size];
15605             }
15606         });
15607         
15608        
15609         
15610         var inputblock = input;
15611         
15612         
15613         
15614         
15615         if (this.before || this.after) {
15616             
15617             inputblock = {
15618                 cls : 'input-group',
15619                 cn :  [] 
15620             };
15621             if (this.before) {
15622                 inputblock.cn.push({
15623                     tag :'span',
15624                     cls : 'input-group-addon',
15625                     html : this.before
15626                 });
15627             }
15628             inputblock.cn.push(input);
15629             if (this.after) {
15630                 inputblock.cn.push({
15631                     tag :'span',
15632                     cls : 'input-group-addon',
15633                     html : this.after
15634                 });
15635             }
15636             
15637         };
15638         
15639         if (align ==='left' && this.fieldLabel.length) {
15640                 Roo.log("left and has label");
15641                 cfg.cn = [
15642                     
15643                     {
15644                         tag: 'label',
15645                         'for' :  id,
15646                         cls : 'control-label col-md-' + this.labelWidth,
15647                         html : this.fieldLabel
15648                         
15649                     },
15650                     {
15651                         cls : "col-md-" + (12 - this.labelWidth), 
15652                         cn: [
15653                             inputblock
15654                         ]
15655                     }
15656                     
15657                 ];
15658         } else if ( this.fieldLabel.length) {
15659                 Roo.log(" label");
15660                 cfg.cn = [
15661                    
15662                     {
15663                         tag: this.boxLabel ? 'span' : 'label',
15664                         'for': id,
15665                         cls: 'control-label box-input-label',
15666                         //cls : 'input-group-addon',
15667                         html : this.fieldLabel
15668                         
15669                     },
15670                     
15671                     inputblock
15672                     
15673                 ];
15674
15675         } else {
15676             
15677                 Roo.log(" no label && no align");
15678                 cfg.cn = [  inputblock ] ;
15679                 
15680                 
15681         };
15682          if(this.boxLabel){
15683             cfg.cn.push( {
15684                 tag: 'label',
15685                 'for': id,
15686                 cls: 'box-label',
15687                 html: this.boxLabel
15688                 
15689             });
15690         }
15691         
15692         
15693        
15694         return cfg;
15695         
15696     },
15697     
15698     /**
15699      * return the real input element.
15700      */
15701     inputEl: function ()
15702     {
15703         return this.el.select('input.roo-checkbox',true).first();
15704     },
15705     
15706     label: function()
15707     {
15708         return this.el.select('label.control-label',true).first();
15709     },
15710     
15711     initEvents : function()
15712     {
15713 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
15714         
15715         this.inputEl().on('click', this.onClick,  this);
15716         
15717     },
15718     
15719     onClick : function()
15720     {   
15721         this.setChecked(!this.checked);
15722     },
15723     
15724     setChecked : function(state,suppressEvent)
15725     {
15726         this.checked = state;
15727         
15728         this.inputEl().dom.checked = state;
15729         
15730         if(suppressEvent !== true){
15731             this.fireEvent('check', this, state);
15732         }
15733         
15734         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
15735         
15736     },
15737     
15738     setValue : function(v,suppressEvent)
15739     {
15740         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
15741     }
15742     
15743 });
15744
15745  
15746 /*
15747  * - LGPL
15748  *
15749  * Radio
15750  * 
15751  */
15752
15753 /**
15754  * @class Roo.bootstrap.Radio
15755  * @extends Roo.bootstrap.CheckBox
15756  * Bootstrap Radio class
15757
15758  * @constructor
15759  * Create a new Radio
15760  * @param {Object} config The config object
15761  */
15762
15763 Roo.bootstrap.Radio = function(config){
15764     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
15765    
15766 };
15767
15768 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
15769     
15770     inputType: 'radio',
15771     inputValue: '',
15772     valueOff: '',
15773     
15774     getAutoCreate : function()
15775     {
15776         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
15777         
15778         var id = Roo.id();
15779         
15780         var cfg = {};
15781         
15782         cfg.cls = 'form-group radio' //input-group
15783         
15784         var input =  {
15785             tag: 'input',
15786             id : id,
15787             type : this.inputType,
15788             value : (!this.checked) ? this.valueOff : this.inputValue,
15789             cls : 'roo-radio',
15790             placeholder : this.placeholder || ''
15791             
15792         };
15793           if (this.weight) { // Validity check?
15794             cfg.cls += " radio-" + this.weight;
15795         }
15796         if (this.disabled) {
15797             input.disabled=true;
15798         }
15799         
15800         if(this.checked){
15801             input.checked = this.checked;
15802         }
15803         
15804         if (this.name) {
15805             input.name = this.name;
15806         }
15807         
15808         if (this.size) {
15809             input.cls += ' input-' + this.size;
15810         }
15811         
15812         var settings=this;
15813         ['xs','sm','md','lg'].map(function(size){
15814             if (settings[size]) {
15815                 cfg.cls += ' col-' + size + '-' + settings[size];
15816             }
15817         });
15818         
15819         var inputblock = input;
15820         
15821         if (this.before || this.after) {
15822             
15823             inputblock = {
15824                 cls : 'input-group',
15825                 cn :  [] 
15826             };
15827             if (this.before) {
15828                 inputblock.cn.push({
15829                     tag :'span',
15830                     cls : 'input-group-addon',
15831                     html : this.before
15832                 });
15833             }
15834             inputblock.cn.push(input);
15835             if (this.after) {
15836                 inputblock.cn.push({
15837                     tag :'span',
15838                     cls : 'input-group-addon',
15839                     html : this.after
15840                 });
15841             }
15842             
15843         };
15844         
15845         if (align ==='left' && this.fieldLabel.length) {
15846                 Roo.log("left and has label");
15847                 cfg.cn = [
15848                     
15849                     {
15850                         tag: 'label',
15851                         'for' :  id,
15852                         cls : 'control-label col-md-' + this.labelWidth,
15853                         html : this.fieldLabel
15854                         
15855                     },
15856                     {
15857                         cls : "col-md-" + (12 - this.labelWidth), 
15858                         cn: [
15859                             inputblock
15860                         ]
15861                     }
15862                     
15863                 ];
15864         } else if ( this.fieldLabel.length) {
15865                 Roo.log(" label");
15866                  cfg.cn = [
15867                    
15868                     {
15869                         tag: 'label',
15870                         'for': id,
15871                         cls: 'control-label box-input-label',
15872                         //cls : 'input-group-addon',
15873                         html : this.fieldLabel
15874                         
15875                     },
15876                     
15877                     inputblock
15878                     
15879                 ];
15880
15881         } else {
15882             
15883                    Roo.log(" no label && no align");
15884                 cfg.cn = [
15885                     
15886                         inputblock
15887                     
15888                 ];
15889                 
15890                 
15891         };
15892         
15893         if(this.boxLabel){
15894             cfg.cn.push({
15895                 tag: 'label',
15896                 'for': id,
15897                 cls: 'box-label',
15898                 html: this.boxLabel
15899             })
15900         }
15901         
15902         return cfg;
15903         
15904     },
15905     inputEl: function ()
15906     {
15907         return this.el.select('input.roo-radio',true).first();
15908     },
15909     onClick : function()
15910     {   
15911         this.setChecked(true);
15912     },
15913     
15914     setChecked : function(state,suppressEvent)
15915     {
15916         if(state){
15917             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
15918                 v.dom.checked = false;
15919             });
15920         }
15921         
15922         this.checked = state;
15923         this.inputEl().dom.checked = state;
15924         
15925         if(suppressEvent !== true){
15926             this.fireEvent('check', this, state);
15927         }
15928         
15929         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
15930         
15931     },
15932     
15933     getGroupValue : function()
15934     {
15935         var value = ''
15936         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
15937             if(v.dom.checked == true){
15938                 value = v.dom.value;
15939             }
15940         });
15941         
15942         return value;
15943     },
15944     
15945     /**
15946      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
15947      * @return {Mixed} value The field value
15948      */
15949     getValue : function(){
15950         return this.getGroupValue();
15951     }
15952     
15953 });
15954
15955  
15956 //<script type="text/javascript">
15957
15958 /*
15959  * Based  Ext JS Library 1.1.1
15960  * Copyright(c) 2006-2007, Ext JS, LLC.
15961  * LGPL
15962  *
15963  */
15964  
15965 /**
15966  * @class Roo.HtmlEditorCore
15967  * @extends Roo.Component
15968  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
15969  *
15970  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
15971  */
15972
15973 Roo.HtmlEditorCore = function(config){
15974     
15975     
15976     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
15977     this.addEvents({
15978         /**
15979          * @event initialize
15980          * Fires when the editor is fully initialized (including the iframe)
15981          * @param {Roo.HtmlEditorCore} this
15982          */
15983         initialize: true,
15984         /**
15985          * @event activate
15986          * Fires when the editor is first receives the focus. Any insertion must wait
15987          * until after this event.
15988          * @param {Roo.HtmlEditorCore} this
15989          */
15990         activate: true,
15991          /**
15992          * @event beforesync
15993          * Fires before the textarea is updated with content from the editor iframe. Return false
15994          * to cancel the sync.
15995          * @param {Roo.HtmlEditorCore} this
15996          * @param {String} html
15997          */
15998         beforesync: true,
15999          /**
16000          * @event beforepush
16001          * Fires before the iframe editor is updated with content from the textarea. Return false
16002          * to cancel the push.
16003          * @param {Roo.HtmlEditorCore} this
16004          * @param {String} html
16005          */
16006         beforepush: true,
16007          /**
16008          * @event sync
16009          * Fires when the textarea is updated with content from the editor iframe.
16010          * @param {Roo.HtmlEditorCore} this
16011          * @param {String} html
16012          */
16013         sync: true,
16014          /**
16015          * @event push
16016          * Fires when the iframe editor is updated with content from the textarea.
16017          * @param {Roo.HtmlEditorCore} this
16018          * @param {String} html
16019          */
16020         push: true,
16021         
16022         /**
16023          * @event editorevent
16024          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
16025          * @param {Roo.HtmlEditorCore} this
16026          */
16027         editorevent: true
16028     });
16029      
16030 };
16031
16032
16033 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
16034
16035
16036      /**
16037      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
16038      */
16039     
16040     owner : false,
16041     
16042      /**
16043      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
16044      *                        Roo.resizable.
16045      */
16046     resizable : false,
16047      /**
16048      * @cfg {Number} height (in pixels)
16049      */   
16050     height: 300,
16051    /**
16052      * @cfg {Number} width (in pixels)
16053      */   
16054     width: 500,
16055     
16056     /**
16057      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
16058      * 
16059      */
16060     stylesheets: false,
16061     
16062     // id of frame..
16063     frameId: false,
16064     
16065     // private properties
16066     validationEvent : false,
16067     deferHeight: true,
16068     initialized : false,
16069     activated : false,
16070     sourceEditMode : false,
16071     onFocus : Roo.emptyFn,
16072     iframePad:3,
16073     hideMode:'offsets',
16074     
16075     clearUp: true,
16076     
16077      
16078     
16079
16080     /**
16081      * Protected method that will not generally be called directly. It
16082      * is called when the editor initializes the iframe with HTML contents. Override this method if you
16083      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
16084      */
16085     getDocMarkup : function(){
16086         // body styles..
16087         var st = '';
16088         Roo.log(this.stylesheets);
16089         
16090         // inherit styels from page...?? 
16091         if (this.stylesheets === false) {
16092             
16093             Roo.get(document.head).select('style').each(function(node) {
16094                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16095             });
16096             
16097             Roo.get(document.head).select('link').each(function(node) { 
16098                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16099             });
16100             
16101         } else if (!this.stylesheets.length) {
16102                 // simple..
16103                 st = '<style type="text/css">' +
16104                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16105                    '</style>';
16106         } else {
16107             Roo.each(this.stylesheets, function(s) {
16108                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
16109             });
16110             
16111         }
16112         
16113         st +=  '<style type="text/css">' +
16114             'IMG { cursor: pointer } ' +
16115         '</style>';
16116
16117         
16118         return '<html><head>' + st  +
16119             //<style type="text/css">' +
16120             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16121             //'</style>' +
16122             ' </head><body class="roo-htmleditor-body"></body></html>';
16123     },
16124
16125     // private
16126     onRender : function(ct, position)
16127     {
16128         var _t = this;
16129         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
16130         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
16131         
16132         
16133         this.el.dom.style.border = '0 none';
16134         this.el.dom.setAttribute('tabIndex', -1);
16135         this.el.addClass('x-hidden hide');
16136         
16137         
16138         
16139         if(Roo.isIE){ // fix IE 1px bogus margin
16140             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
16141         }
16142        
16143         
16144         this.frameId = Roo.id();
16145         
16146          
16147         
16148         var iframe = this.owner.wrap.createChild({
16149             tag: 'iframe',
16150             cls: 'form-control', // bootstrap..
16151             id: this.frameId,
16152             name: this.frameId,
16153             frameBorder : 'no',
16154             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
16155         }, this.el
16156         );
16157         
16158         
16159         this.iframe = iframe.dom;
16160
16161          this.assignDocWin();
16162         
16163         this.doc.designMode = 'on';
16164        
16165         this.doc.open();
16166         this.doc.write(this.getDocMarkup());
16167         this.doc.close();
16168
16169         
16170         var task = { // must defer to wait for browser to be ready
16171             run : function(){
16172                 //console.log("run task?" + this.doc.readyState);
16173                 this.assignDocWin();
16174                 if(this.doc.body || this.doc.readyState == 'complete'){
16175                     try {
16176                         this.doc.designMode="on";
16177                     } catch (e) {
16178                         return;
16179                     }
16180                     Roo.TaskMgr.stop(task);
16181                     this.initEditor.defer(10, this);
16182                 }
16183             },
16184             interval : 10,
16185             duration: 10000,
16186             scope: this
16187         };
16188         Roo.TaskMgr.start(task);
16189
16190         
16191          
16192     },
16193
16194     // private
16195     onResize : function(w, h)
16196     {
16197          Roo.log('resize: ' +w + ',' + h );
16198         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
16199         if(!this.iframe){
16200             return;
16201         }
16202         if(typeof w == 'number'){
16203             
16204             this.iframe.style.width = w + 'px';
16205         }
16206         if(typeof h == 'number'){
16207             
16208             this.iframe.style.height = h + 'px';
16209             if(this.doc){
16210                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
16211             }
16212         }
16213         
16214     },
16215
16216     /**
16217      * Toggles the editor between standard and source edit mode.
16218      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
16219      */
16220     toggleSourceEdit : function(sourceEditMode){
16221         
16222         this.sourceEditMode = sourceEditMode === true;
16223         
16224         if(this.sourceEditMode){
16225  
16226             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
16227             
16228         }else{
16229             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
16230             //this.iframe.className = '';
16231             this.deferFocus();
16232         }
16233         //this.setSize(this.owner.wrap.getSize());
16234         //this.fireEvent('editmodechange', this, this.sourceEditMode);
16235     },
16236
16237     
16238   
16239
16240     /**
16241      * Protected method that will not generally be called directly. If you need/want
16242      * custom HTML cleanup, this is the method you should override.
16243      * @param {String} html The HTML to be cleaned
16244      * return {String} The cleaned HTML
16245      */
16246     cleanHtml : function(html){
16247         html = String(html);
16248         if(html.length > 5){
16249             if(Roo.isSafari){ // strip safari nonsense
16250                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
16251             }
16252         }
16253         if(html == '&nbsp;'){
16254             html = '';
16255         }
16256         return html;
16257     },
16258
16259     /**
16260      * HTML Editor -> Textarea
16261      * Protected method that will not generally be called directly. Syncs the contents
16262      * of the editor iframe with the textarea.
16263      */
16264     syncValue : function(){
16265         if(this.initialized){
16266             var bd = (this.doc.body || this.doc.documentElement);
16267             //this.cleanUpPaste(); -- this is done else where and causes havoc..
16268             var html = bd.innerHTML;
16269             if(Roo.isSafari){
16270                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
16271                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
16272                 if(m && m[1]){
16273                     html = '<div style="'+m[0]+'">' + html + '</div>';
16274                 }
16275             }
16276             html = this.cleanHtml(html);
16277             // fix up the special chars.. normaly like back quotes in word...
16278             // however we do not want to do this with chinese..
16279             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
16280                 var cc = b.charCodeAt();
16281                 if (
16282                     (cc >= 0x4E00 && cc < 0xA000 ) ||
16283                     (cc >= 0x3400 && cc < 0x4E00 ) ||
16284                     (cc >= 0xf900 && cc < 0xfb00 )
16285                 ) {
16286                         return b;
16287                 }
16288                 return "&#"+cc+";" 
16289             });
16290             if(this.owner.fireEvent('beforesync', this, html) !== false){
16291                 this.el.dom.value = html;
16292                 this.owner.fireEvent('sync', this, html);
16293             }
16294         }
16295     },
16296
16297     /**
16298      * Protected method that will not generally be called directly. Pushes the value of the textarea
16299      * into the iframe editor.
16300      */
16301     pushValue : function(){
16302         if(this.initialized){
16303             var v = this.el.dom.value.trim();
16304             
16305 //            if(v.length < 1){
16306 //                v = '&#160;';
16307 //            }
16308             
16309             if(this.owner.fireEvent('beforepush', this, v) !== false){
16310                 var d = (this.doc.body || this.doc.documentElement);
16311                 d.innerHTML = v;
16312                 this.cleanUpPaste();
16313                 this.el.dom.value = d.innerHTML;
16314                 this.owner.fireEvent('push', this, v);
16315             }
16316         }
16317     },
16318
16319     // private
16320     deferFocus : function(){
16321         this.focus.defer(10, this);
16322     },
16323
16324     // doc'ed in Field
16325     focus : function(){
16326         if(this.win && !this.sourceEditMode){
16327             this.win.focus();
16328         }else{
16329             this.el.focus();
16330         }
16331     },
16332     
16333     assignDocWin: function()
16334     {
16335         var iframe = this.iframe;
16336         
16337          if(Roo.isIE){
16338             this.doc = iframe.contentWindow.document;
16339             this.win = iframe.contentWindow;
16340         } else {
16341             if (!Roo.get(this.frameId)) {
16342                 return;
16343             }
16344             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
16345             this.win = Roo.get(this.frameId).dom.contentWindow;
16346         }
16347     },
16348     
16349     // private
16350     initEditor : function(){
16351         //console.log("INIT EDITOR");
16352         this.assignDocWin();
16353         
16354         
16355         
16356         this.doc.designMode="on";
16357         this.doc.open();
16358         this.doc.write(this.getDocMarkup());
16359         this.doc.close();
16360         
16361         var dbody = (this.doc.body || this.doc.documentElement);
16362         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
16363         // this copies styles from the containing element into thsi one..
16364         // not sure why we need all of this..
16365         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
16366         
16367         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
16368         //ss['background-attachment'] = 'fixed'; // w3c
16369         dbody.bgProperties = 'fixed'; // ie
16370         //Roo.DomHelper.applyStyles(dbody, ss);
16371         Roo.EventManager.on(this.doc, {
16372             //'mousedown': this.onEditorEvent,
16373             'mouseup': this.onEditorEvent,
16374             'dblclick': this.onEditorEvent,
16375             'click': this.onEditorEvent,
16376             'keyup': this.onEditorEvent,
16377             buffer:100,
16378             scope: this
16379         });
16380         if(Roo.isGecko){
16381             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
16382         }
16383         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
16384             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
16385         }
16386         this.initialized = true;
16387
16388         this.owner.fireEvent('initialize', this);
16389         this.pushValue();
16390     },
16391
16392     // private
16393     onDestroy : function(){
16394         
16395         
16396         
16397         if(this.rendered){
16398             
16399             //for (var i =0; i < this.toolbars.length;i++) {
16400             //    // fixme - ask toolbars for heights?
16401             //    this.toolbars[i].onDestroy();
16402            // }
16403             
16404             //this.wrap.dom.innerHTML = '';
16405             //this.wrap.remove();
16406         }
16407     },
16408
16409     // private
16410     onFirstFocus : function(){
16411         
16412         this.assignDocWin();
16413         
16414         
16415         this.activated = true;
16416          
16417     
16418         if(Roo.isGecko){ // prevent silly gecko errors
16419             this.win.focus();
16420             var s = this.win.getSelection();
16421             if(!s.focusNode || s.focusNode.nodeType != 3){
16422                 var r = s.getRangeAt(0);
16423                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
16424                 r.collapse(true);
16425                 this.deferFocus();
16426             }
16427             try{
16428                 this.execCmd('useCSS', true);
16429                 this.execCmd('styleWithCSS', false);
16430             }catch(e){}
16431         }
16432         this.owner.fireEvent('activate', this);
16433     },
16434
16435     // private
16436     adjustFont: function(btn){
16437         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
16438         //if(Roo.isSafari){ // safari
16439         //    adjust *= 2;
16440        // }
16441         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
16442         if(Roo.isSafari){ // safari
16443             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
16444             v =  (v < 10) ? 10 : v;
16445             v =  (v > 48) ? 48 : v;
16446             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
16447             
16448         }
16449         
16450         
16451         v = Math.max(1, v+adjust);
16452         
16453         this.execCmd('FontSize', v  );
16454     },
16455
16456     onEditorEvent : function(e){
16457         this.owner.fireEvent('editorevent', this, e);
16458       //  this.updateToolbar();
16459         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
16460     },
16461
16462     insertTag : function(tg)
16463     {
16464         // could be a bit smarter... -> wrap the current selected tRoo..
16465         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
16466             
16467             range = this.createRange(this.getSelection());
16468             var wrappingNode = this.doc.createElement(tg.toLowerCase());
16469             wrappingNode.appendChild(range.extractContents());
16470             range.insertNode(wrappingNode);
16471
16472             return;
16473             
16474             
16475             
16476         }
16477         this.execCmd("formatblock",   tg);
16478         
16479     },
16480     
16481     insertText : function(txt)
16482     {
16483         
16484         
16485         var range = this.createRange();
16486         range.deleteContents();
16487                //alert(Sender.getAttribute('label'));
16488                
16489         range.insertNode(this.doc.createTextNode(txt));
16490     } ,
16491     
16492      
16493
16494     /**
16495      * Executes a Midas editor command on the editor document and performs necessary focus and
16496      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
16497      * @param {String} cmd The Midas command
16498      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16499      */
16500     relayCmd : function(cmd, value){
16501         this.win.focus();
16502         this.execCmd(cmd, value);
16503         this.owner.fireEvent('editorevent', this);
16504         //this.updateToolbar();
16505         this.owner.deferFocus();
16506     },
16507
16508     /**
16509      * Executes a Midas editor command directly on the editor document.
16510      * For visual commands, you should use {@link #relayCmd} instead.
16511      * <b>This should only be called after the editor is initialized.</b>
16512      * @param {String} cmd The Midas command
16513      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16514      */
16515     execCmd : function(cmd, value){
16516         this.doc.execCommand(cmd, false, value === undefined ? null : value);
16517         this.syncValue();
16518     },
16519  
16520  
16521    
16522     /**
16523      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
16524      * to insert tRoo.
16525      * @param {String} text | dom node.. 
16526      */
16527     insertAtCursor : function(text)
16528     {
16529         
16530         
16531         
16532         if(!this.activated){
16533             return;
16534         }
16535         /*
16536         if(Roo.isIE){
16537             this.win.focus();
16538             var r = this.doc.selection.createRange();
16539             if(r){
16540                 r.collapse(true);
16541                 r.pasteHTML(text);
16542                 this.syncValue();
16543                 this.deferFocus();
16544             
16545             }
16546             return;
16547         }
16548         */
16549         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
16550             this.win.focus();
16551             
16552             
16553             // from jquery ui (MIT licenced)
16554             var range, node;
16555             var win = this.win;
16556             
16557             if (win.getSelection && win.getSelection().getRangeAt) {
16558                 range = win.getSelection().getRangeAt(0);
16559                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
16560                 range.insertNode(node);
16561             } else if (win.document.selection && win.document.selection.createRange) {
16562                 // no firefox support
16563                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
16564                 win.document.selection.createRange().pasteHTML(txt);
16565             } else {
16566                 // no firefox support
16567                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
16568                 this.execCmd('InsertHTML', txt);
16569             } 
16570             
16571             this.syncValue();
16572             
16573             this.deferFocus();
16574         }
16575     },
16576  // private
16577     mozKeyPress : function(e){
16578         if(e.ctrlKey){
16579             var c = e.getCharCode(), cmd;
16580           
16581             if(c > 0){
16582                 c = String.fromCharCode(c).toLowerCase();
16583                 switch(c){
16584                     case 'b':
16585                         cmd = 'bold';
16586                         break;
16587                     case 'i':
16588                         cmd = 'italic';
16589                         break;
16590                     
16591                     case 'u':
16592                         cmd = 'underline';
16593                         break;
16594                     
16595                     case 'v':
16596                         this.cleanUpPaste.defer(100, this);
16597                         return;
16598                         
16599                 }
16600                 if(cmd){
16601                     this.win.focus();
16602                     this.execCmd(cmd);
16603                     this.deferFocus();
16604                     e.preventDefault();
16605                 }
16606                 
16607             }
16608         }
16609     },
16610
16611     // private
16612     fixKeys : function(){ // load time branching for fastest keydown performance
16613         if(Roo.isIE){
16614             return function(e){
16615                 var k = e.getKey(), r;
16616                 if(k == e.TAB){
16617                     e.stopEvent();
16618                     r = this.doc.selection.createRange();
16619                     if(r){
16620                         r.collapse(true);
16621                         r.pasteHTML('&#160;&#160;&#160;&#160;');
16622                         this.deferFocus();
16623                     }
16624                     return;
16625                 }
16626                 
16627                 if(k == e.ENTER){
16628                     r = this.doc.selection.createRange();
16629                     if(r){
16630                         var target = r.parentElement();
16631                         if(!target || target.tagName.toLowerCase() != 'li'){
16632                             e.stopEvent();
16633                             r.pasteHTML('<br />');
16634                             r.collapse(false);
16635                             r.select();
16636                         }
16637                     }
16638                 }
16639                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
16640                     this.cleanUpPaste.defer(100, this);
16641                     return;
16642                 }
16643                 
16644                 
16645             };
16646         }else if(Roo.isOpera){
16647             return function(e){
16648                 var k = e.getKey();
16649                 if(k == e.TAB){
16650                     e.stopEvent();
16651                     this.win.focus();
16652                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
16653                     this.deferFocus();
16654                 }
16655                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
16656                     this.cleanUpPaste.defer(100, this);
16657                     return;
16658                 }
16659                 
16660             };
16661         }else if(Roo.isSafari){
16662             return function(e){
16663                 var k = e.getKey();
16664                 
16665                 if(k == e.TAB){
16666                     e.stopEvent();
16667                     this.execCmd('InsertText','\t');
16668                     this.deferFocus();
16669                     return;
16670                 }
16671                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
16672                     this.cleanUpPaste.defer(100, this);
16673                     return;
16674                 }
16675                 
16676              };
16677         }
16678     }(),
16679     
16680     getAllAncestors: function()
16681     {
16682         var p = this.getSelectedNode();
16683         var a = [];
16684         if (!p) {
16685             a.push(p); // push blank onto stack..
16686             p = this.getParentElement();
16687         }
16688         
16689         
16690         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
16691             a.push(p);
16692             p = p.parentNode;
16693         }
16694         a.push(this.doc.body);
16695         return a;
16696     },
16697     lastSel : false,
16698     lastSelNode : false,
16699     
16700     
16701     getSelection : function() 
16702     {
16703         this.assignDocWin();
16704         return Roo.isIE ? this.doc.selection : this.win.getSelection();
16705     },
16706     
16707     getSelectedNode: function() 
16708     {
16709         // this may only work on Gecko!!!
16710         
16711         // should we cache this!!!!
16712         
16713         
16714         
16715          
16716         var range = this.createRange(this.getSelection()).cloneRange();
16717         
16718         if (Roo.isIE) {
16719             var parent = range.parentElement();
16720             while (true) {
16721                 var testRange = range.duplicate();
16722                 testRange.moveToElementText(parent);
16723                 if (testRange.inRange(range)) {
16724                     break;
16725                 }
16726                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
16727                     break;
16728                 }
16729                 parent = parent.parentElement;
16730             }
16731             return parent;
16732         }
16733         
16734         // is ancestor a text element.
16735         var ac =  range.commonAncestorContainer;
16736         if (ac.nodeType == 3) {
16737             ac = ac.parentNode;
16738         }
16739         
16740         var ar = ac.childNodes;
16741          
16742         var nodes = [];
16743         var other_nodes = [];
16744         var has_other_nodes = false;
16745         for (var i=0;i<ar.length;i++) {
16746             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
16747                 continue;
16748             }
16749             // fullly contained node.
16750             
16751             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
16752                 nodes.push(ar[i]);
16753                 continue;
16754             }
16755             
16756             // probably selected..
16757             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
16758                 other_nodes.push(ar[i]);
16759                 continue;
16760             }
16761             // outer..
16762             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
16763                 continue;
16764             }
16765             
16766             
16767             has_other_nodes = true;
16768         }
16769         if (!nodes.length && other_nodes.length) {
16770             nodes= other_nodes;
16771         }
16772         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
16773             return false;
16774         }
16775         
16776         return nodes[0];
16777     },
16778     createRange: function(sel)
16779     {
16780         // this has strange effects when using with 
16781         // top toolbar - not sure if it's a great idea.
16782         //this.editor.contentWindow.focus();
16783         if (typeof sel != "undefined") {
16784             try {
16785                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
16786             } catch(e) {
16787                 return this.doc.createRange();
16788             }
16789         } else {
16790             return this.doc.createRange();
16791         }
16792     },
16793     getParentElement: function()
16794     {
16795         
16796         this.assignDocWin();
16797         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
16798         
16799         var range = this.createRange(sel);
16800          
16801         try {
16802             var p = range.commonAncestorContainer;
16803             while (p.nodeType == 3) { // text node
16804                 p = p.parentNode;
16805             }
16806             return p;
16807         } catch (e) {
16808             return null;
16809         }
16810     
16811     },
16812     /***
16813      *
16814      * Range intersection.. the hard stuff...
16815      *  '-1' = before
16816      *  '0' = hits..
16817      *  '1' = after.
16818      *         [ -- selected range --- ]
16819      *   [fail]                        [fail]
16820      *
16821      *    basically..
16822      *      if end is before start or  hits it. fail.
16823      *      if start is after end or hits it fail.
16824      *
16825      *   if either hits (but other is outside. - then it's not 
16826      *   
16827      *    
16828      **/
16829     
16830     
16831     // @see http://www.thismuchiknow.co.uk/?p=64.
16832     rangeIntersectsNode : function(range, node)
16833     {
16834         var nodeRange = node.ownerDocument.createRange();
16835         try {
16836             nodeRange.selectNode(node);
16837         } catch (e) {
16838             nodeRange.selectNodeContents(node);
16839         }
16840     
16841         var rangeStartRange = range.cloneRange();
16842         rangeStartRange.collapse(true);
16843     
16844         var rangeEndRange = range.cloneRange();
16845         rangeEndRange.collapse(false);
16846     
16847         var nodeStartRange = nodeRange.cloneRange();
16848         nodeStartRange.collapse(true);
16849     
16850         var nodeEndRange = nodeRange.cloneRange();
16851         nodeEndRange.collapse(false);
16852     
16853         return rangeStartRange.compareBoundaryPoints(
16854                  Range.START_TO_START, nodeEndRange) == -1 &&
16855                rangeEndRange.compareBoundaryPoints(
16856                  Range.START_TO_START, nodeStartRange) == 1;
16857         
16858          
16859     },
16860     rangeCompareNode : function(range, node)
16861     {
16862         var nodeRange = node.ownerDocument.createRange();
16863         try {
16864             nodeRange.selectNode(node);
16865         } catch (e) {
16866             nodeRange.selectNodeContents(node);
16867         }
16868         
16869         
16870         range.collapse(true);
16871     
16872         nodeRange.collapse(true);
16873      
16874         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
16875         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
16876          
16877         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
16878         
16879         var nodeIsBefore   =  ss == 1;
16880         var nodeIsAfter    = ee == -1;
16881         
16882         if (nodeIsBefore && nodeIsAfter)
16883             return 0; // outer
16884         if (!nodeIsBefore && nodeIsAfter)
16885             return 1; //right trailed.
16886         
16887         if (nodeIsBefore && !nodeIsAfter)
16888             return 2;  // left trailed.
16889         // fully contined.
16890         return 3;
16891     },
16892
16893     // private? - in a new class?
16894     cleanUpPaste :  function()
16895     {
16896         // cleans up the whole document..
16897         Roo.log('cleanuppaste');
16898         
16899         this.cleanUpChildren(this.doc.body);
16900         var clean = this.cleanWordChars(this.doc.body.innerHTML);
16901         if (clean != this.doc.body.innerHTML) {
16902             this.doc.body.innerHTML = clean;
16903         }
16904         
16905     },
16906     
16907     cleanWordChars : function(input) {// change the chars to hex code
16908         var he = Roo.HtmlEditorCore;
16909         
16910         var output = input;
16911         Roo.each(he.swapCodes, function(sw) { 
16912             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
16913             
16914             output = output.replace(swapper, sw[1]);
16915         });
16916         
16917         return output;
16918     },
16919     
16920     
16921     cleanUpChildren : function (n)
16922     {
16923         if (!n.childNodes.length) {
16924             return;
16925         }
16926         for (var i = n.childNodes.length-1; i > -1 ; i--) {
16927            this.cleanUpChild(n.childNodes[i]);
16928         }
16929     },
16930     
16931     
16932         
16933     
16934     cleanUpChild : function (node)
16935     {
16936         var ed = this;
16937         //console.log(node);
16938         if (node.nodeName == "#text") {
16939             // clean up silly Windows -- stuff?
16940             return; 
16941         }
16942         if (node.nodeName == "#comment") {
16943             node.parentNode.removeChild(node);
16944             // clean up silly Windows -- stuff?
16945             return; 
16946         }
16947         
16948         if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1 && this.clearUp) {
16949             // remove node.
16950             node.parentNode.removeChild(node);
16951             return;
16952             
16953         }
16954         
16955         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
16956         
16957         // remove <a name=....> as rendering on yahoo mailer is borked with this.
16958         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
16959         
16960         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
16961         //    remove_keep_children = true;
16962         //}
16963         
16964         if (remove_keep_children) {
16965             this.cleanUpChildren(node);
16966             // inserts everything just before this node...
16967             while (node.childNodes.length) {
16968                 var cn = node.childNodes[0];
16969                 node.removeChild(cn);
16970                 node.parentNode.insertBefore(cn, node);
16971             }
16972             node.parentNode.removeChild(node);
16973             return;
16974         }
16975         
16976         if (!node.attributes || !node.attributes.length) {
16977             this.cleanUpChildren(node);
16978             return;
16979         }
16980         
16981         function cleanAttr(n,v)
16982         {
16983             
16984             if (v.match(/^\./) || v.match(/^\//)) {
16985                 return;
16986             }
16987             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
16988                 return;
16989             }
16990             if (v.match(/^#/)) {
16991                 return;
16992             }
16993 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
16994             node.removeAttribute(n);
16995             
16996         }
16997         
16998         function cleanStyle(n,v)
16999         {
17000             if (v.match(/expression/)) { //XSS?? should we even bother..
17001                 node.removeAttribute(n);
17002                 return;
17003             }
17004             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
17005             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
17006             
17007             
17008             var parts = v.split(/;/);
17009             var clean = [];
17010             
17011             Roo.each(parts, function(p) {
17012                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
17013                 if (!p.length) {
17014                     return true;
17015                 }
17016                 var l = p.split(':').shift().replace(/\s+/g,'');
17017                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
17018                 
17019                 if ( cblack.indexOf(l) > -1) {
17020 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17021                     //node.removeAttribute(n);
17022                     return true;
17023                 }
17024                 //Roo.log()
17025                 // only allow 'c whitelisted system attributes'
17026                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
17027 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17028                     //node.removeAttribute(n);
17029                     return true;
17030                 }
17031                 
17032                 
17033                  
17034                 
17035                 clean.push(p);
17036                 return true;
17037             });
17038             if (clean.length) { 
17039                 node.setAttribute(n, clean.join(';'));
17040             } else {
17041                 node.removeAttribute(n);
17042             }
17043             
17044         }
17045         
17046         
17047         for (var i = node.attributes.length-1; i > -1 ; i--) {
17048             var a = node.attributes[i];
17049             //console.log(a);
17050             
17051             if (a.name.toLowerCase().substr(0,2)=='on')  {
17052                 node.removeAttribute(a.name);
17053                 continue;
17054             }
17055             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
17056                 node.removeAttribute(a.name);
17057                 continue;
17058             }
17059             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
17060                 cleanAttr(a.name,a.value); // fixme..
17061                 continue;
17062             }
17063             if (a.name == 'style') {
17064                 cleanStyle(a.name,a.value);
17065                 continue;
17066             }
17067             /// clean up MS crap..
17068             // tecnically this should be a list of valid class'es..
17069             
17070             
17071             if (a.name == 'class') {
17072                 if (a.value.match(/^Mso/)) {
17073                     node.className = '';
17074                 }
17075                 
17076                 if (a.value.match(/body/)) {
17077                     node.className = '';
17078                 }
17079                 continue;
17080             }
17081             
17082             // style cleanup!?
17083             // class cleanup?
17084             
17085         }
17086         
17087         
17088         this.cleanUpChildren(node);
17089         
17090         
17091     },
17092     /**
17093      * Clean up MS wordisms...
17094      */
17095     cleanWord : function(node)
17096     {
17097         var _t = this;
17098         var cleanWordChildren = function()
17099         {
17100             if (!node.childNodes.length) {
17101                 return;
17102             }
17103             for (var i = node.childNodes.length-1; i > -1 ; i--) {
17104                _t.cleanWord(node.childNodes[i]);
17105             }
17106         }
17107         
17108         
17109         if (!node) {
17110             this.cleanWord(this.doc.body);
17111             return;
17112         }
17113         if (node.nodeName == "#text") {
17114             // clean up silly Windows -- stuff?
17115             return; 
17116         }
17117         if (node.nodeName == "#comment") {
17118             node.parentNode.removeChild(node);
17119             // clean up silly Windows -- stuff?
17120             return; 
17121         }
17122         
17123         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
17124             node.parentNode.removeChild(node);
17125             return;
17126         }
17127         
17128         // remove - but keep children..
17129         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
17130             while (node.childNodes.length) {
17131                 var cn = node.childNodes[0];
17132                 node.removeChild(cn);
17133                 node.parentNode.insertBefore(cn, node);
17134             }
17135             node.parentNode.removeChild(node);
17136             cleanWordChildren();
17137             return;
17138         }
17139         // clean styles
17140         if (node.className.length) {
17141             
17142             var cn = node.className.split(/\W+/);
17143             var cna = [];
17144             Roo.each(cn, function(cls) {
17145                 if (cls.match(/Mso[a-zA-Z]+/)) {
17146                     return;
17147                 }
17148                 cna.push(cls);
17149             });
17150             node.className = cna.length ? cna.join(' ') : '';
17151             if (!cna.length) {
17152                 node.removeAttribute("class");
17153             }
17154         }
17155         
17156         if (node.hasAttribute("lang")) {
17157             node.removeAttribute("lang");
17158         }
17159         
17160         if (node.hasAttribute("style")) {
17161             
17162             var styles = node.getAttribute("style").split(";");
17163             var nstyle = [];
17164             Roo.each(styles, function(s) {
17165                 if (!s.match(/:/)) {
17166                     return;
17167                 }
17168                 var kv = s.split(":");
17169                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
17170                     return;
17171                 }
17172                 // what ever is left... we allow.
17173                 nstyle.push(s);
17174             });
17175             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
17176             if (!nstyle.length) {
17177                 node.removeAttribute('style');
17178             }
17179         }
17180         
17181         cleanWordChildren();
17182         
17183         
17184     },
17185     domToHTML : function(currentElement, depth, nopadtext) {
17186         
17187             depth = depth || 0;
17188             nopadtext = nopadtext || false;
17189         
17190             if (!currentElement) {
17191                 return this.domToHTML(this.doc.body);
17192             }
17193             
17194             //Roo.log(currentElement);
17195             var j;
17196             var allText = false;
17197             var nodeName = currentElement.nodeName;
17198             var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
17199             
17200             if  (nodeName == '#text') {
17201                 return currentElement.nodeValue;
17202             }
17203             
17204             
17205             var ret = '';
17206             if (nodeName != 'BODY') {
17207                  
17208                 var i = 0;
17209                 // Prints the node tagName, such as <A>, <IMG>, etc
17210                 if (tagName) {
17211                     var attr = [];
17212                     for(i = 0; i < currentElement.attributes.length;i++) {
17213                         // quoting?
17214                         var aname = currentElement.attributes.item(i).name;
17215                         if (!currentElement.attributes.item(i).value.length) {
17216                             continue;
17217                         }
17218                         attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
17219                     }
17220                     
17221                     ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
17222                 } 
17223                 else {
17224                     
17225                     // eack
17226                 }
17227             } else {
17228                 tagName = false;
17229             }
17230             if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
17231                 return ret;
17232             }
17233             if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
17234                 nopadtext = true;
17235             }
17236             
17237             
17238             // Traverse the tree
17239             i = 0;
17240             var currentElementChild = currentElement.childNodes.item(i);
17241             var allText = true;
17242             var innerHTML  = '';
17243             lastnode = '';
17244             while (currentElementChild) {
17245                 // Formatting code (indent the tree so it looks nice on the screen)
17246                 var nopad = nopadtext;
17247                 if (lastnode == 'SPAN') {
17248                     nopad  = true;
17249                 }
17250                 // text
17251                 if  (currentElementChild.nodeName == '#text') {
17252                     var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
17253                     if (!nopad && toadd.length > 80) {
17254                         innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
17255                     }
17256                     innerHTML  += toadd;
17257                     
17258                     i++;
17259                     currentElementChild = currentElement.childNodes.item(i);
17260                     lastNode = '';
17261                     continue;
17262                 }
17263                 allText = false;
17264                 
17265                 innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
17266                     
17267                 // Recursively traverse the tree structure of the child node
17268                 innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
17269                 lastnode = currentElementChild.nodeName;
17270                 i++;
17271                 currentElementChild=currentElement.childNodes.item(i);
17272             }
17273             
17274             ret += innerHTML;
17275             
17276             if (!allText) {
17277                     // The remaining code is mostly for formatting the tree
17278                 ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
17279             }
17280             
17281             
17282             if (tagName) {
17283                 ret+= "</"+tagName+">";
17284             }
17285             return ret;
17286             
17287         }
17288     
17289     // hide stuff that is not compatible
17290     /**
17291      * @event blur
17292      * @hide
17293      */
17294     /**
17295      * @event change
17296      * @hide
17297      */
17298     /**
17299      * @event focus
17300      * @hide
17301      */
17302     /**
17303      * @event specialkey
17304      * @hide
17305      */
17306     /**
17307      * @cfg {String} fieldClass @hide
17308      */
17309     /**
17310      * @cfg {String} focusClass @hide
17311      */
17312     /**
17313      * @cfg {String} autoCreate @hide
17314      */
17315     /**
17316      * @cfg {String} inputType @hide
17317      */
17318     /**
17319      * @cfg {String} invalidClass @hide
17320      */
17321     /**
17322      * @cfg {String} invalidText @hide
17323      */
17324     /**
17325      * @cfg {String} msgFx @hide
17326      */
17327     /**
17328      * @cfg {String} validateOnBlur @hide
17329      */
17330 });
17331
17332 Roo.HtmlEditorCore.white = [
17333         'area', 'br', 'img', 'input', 'hr', 'wbr',
17334         
17335        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
17336        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
17337        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
17338        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
17339        'table',   'ul',         'xmp', 
17340        
17341        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
17342       'thead',   'tr', 
17343      
17344       'dir', 'menu', 'ol', 'ul', 'dl',
17345        
17346       'embed',  'object'
17347 ];
17348
17349
17350 Roo.HtmlEditorCore.black = [
17351     //    'embed',  'object', // enable - backend responsiblity to clean thiese
17352         'applet', // 
17353         'base',   'basefont', 'bgsound', 'blink',  'body', 
17354         'frame',  'frameset', 'head',    'html',   'ilayer', 
17355         'iframe', 'layer',  'link',     'meta',    'object',   
17356         'script', 'style' ,'title',  'xml' // clean later..
17357 ];
17358 Roo.HtmlEditorCore.clean = [
17359     'script', 'style', 'title', 'xml'
17360 ];
17361 Roo.HtmlEditorCore.remove = [
17362     'font'
17363 ];
17364 // attributes..
17365
17366 Roo.HtmlEditorCore.ablack = [
17367     'on'
17368 ];
17369     
17370 Roo.HtmlEditorCore.aclean = [ 
17371     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
17372 ];
17373
17374 // protocols..
17375 Roo.HtmlEditorCore.pwhite= [
17376         'http',  'https',  'mailto'
17377 ];
17378
17379 // white listed style attributes.
17380 Roo.HtmlEditorCore.cwhite= [
17381       //  'text-align', /// default is to allow most things..
17382       
17383          
17384 //        'font-size'//??
17385 ];
17386
17387 // black listed style attributes.
17388 Roo.HtmlEditorCore.cblack= [
17389       //  'font-size' -- this can be set by the project 
17390 ];
17391
17392
17393 Roo.HtmlEditorCore.swapCodes   =[ 
17394     [    8211, "--" ], 
17395     [    8212, "--" ], 
17396     [    8216,  "'" ],  
17397     [    8217, "'" ],  
17398     [    8220, '"' ],  
17399     [    8221, '"' ],  
17400     [    8226, "*" ],  
17401     [    8230, "..." ]
17402 ]; 
17403
17404     /*
17405  * - LGPL
17406  *
17407  * HtmlEditor
17408  * 
17409  */
17410
17411 /**
17412  * @class Roo.bootstrap.HtmlEditor
17413  * @extends Roo.bootstrap.TextArea
17414  * Bootstrap HtmlEditor class
17415
17416  * @constructor
17417  * Create a new HtmlEditor
17418  * @param {Object} config The config object
17419  */
17420
17421 Roo.bootstrap.HtmlEditor = function(config){
17422     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
17423     if (!this.toolbars) {
17424         this.toolbars = [];
17425     }
17426     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
17427     this.addEvents({
17428             /**
17429              * @event initialize
17430              * Fires when the editor is fully initialized (including the iframe)
17431              * @param {HtmlEditor} this
17432              */
17433             initialize: true,
17434             /**
17435              * @event activate
17436              * Fires when the editor is first receives the focus. Any insertion must wait
17437              * until after this event.
17438              * @param {HtmlEditor} this
17439              */
17440             activate: true,
17441              /**
17442              * @event beforesync
17443              * Fires before the textarea is updated with content from the editor iframe. Return false
17444              * to cancel the sync.
17445              * @param {HtmlEditor} this
17446              * @param {String} html
17447              */
17448             beforesync: true,
17449              /**
17450              * @event beforepush
17451              * Fires before the iframe editor is updated with content from the textarea. Return false
17452              * to cancel the push.
17453              * @param {HtmlEditor} this
17454              * @param {String} html
17455              */
17456             beforepush: true,
17457              /**
17458              * @event sync
17459              * Fires when the textarea is updated with content from the editor iframe.
17460              * @param {HtmlEditor} this
17461              * @param {String} html
17462              */
17463             sync: true,
17464              /**
17465              * @event push
17466              * Fires when the iframe editor is updated with content from the textarea.
17467              * @param {HtmlEditor} this
17468              * @param {String} html
17469              */
17470             push: true,
17471              /**
17472              * @event editmodechange
17473              * Fires when the editor switches edit modes
17474              * @param {HtmlEditor} this
17475              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
17476              */
17477             editmodechange: true,
17478             /**
17479              * @event editorevent
17480              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
17481              * @param {HtmlEditor} this
17482              */
17483             editorevent: true,
17484             /**
17485              * @event firstfocus
17486              * Fires when on first focus - needed by toolbars..
17487              * @param {HtmlEditor} this
17488              */
17489             firstfocus: true,
17490             /**
17491              * @event autosave
17492              * Auto save the htmlEditor value as a file into Events
17493              * @param {HtmlEditor} this
17494              */
17495             autosave: true,
17496             /**
17497              * @event savedpreview
17498              * preview the saved version of htmlEditor
17499              * @param {HtmlEditor} this
17500              */
17501             savedpreview: true
17502         });
17503 };
17504
17505
17506 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
17507     
17508     
17509       /**
17510      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
17511      */
17512     toolbars : false,
17513    
17514      /**
17515      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
17516      *                        Roo.resizable.
17517      */
17518     resizable : false,
17519      /**
17520      * @cfg {Number} height (in pixels)
17521      */   
17522     height: 300,
17523    /**
17524      * @cfg {Number} width (in pixels)
17525      */   
17526     width: false,
17527     
17528     /**
17529      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
17530      * 
17531      */
17532     stylesheets: false,
17533     
17534     // id of frame..
17535     frameId: false,
17536     
17537     // private properties
17538     validationEvent : false,
17539     deferHeight: true,
17540     initialized : false,
17541     activated : false,
17542     
17543     onFocus : Roo.emptyFn,
17544     iframePad:3,
17545     hideMode:'offsets',
17546     
17547     
17548     tbContainer : false,
17549     
17550     toolbarContainer :function() {
17551         return this.wrap.select('.x-html-editor-tb',true).first();
17552     },
17553
17554     /**
17555      * Protected method that will not generally be called directly. It
17556      * is called when the editor creates its toolbar. Override this method if you need to
17557      * add custom toolbar buttons.
17558      * @param {HtmlEditor} editor
17559      */
17560     createToolbar : function(){
17561         
17562         Roo.log("create toolbars");
17563         
17564         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
17565         this.toolbars[0].render(this.toolbarContainer());
17566         
17567         return;
17568         
17569 //        if (!editor.toolbars || !editor.toolbars.length) {
17570 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
17571 //        }
17572 //        
17573 //        for (var i =0 ; i < editor.toolbars.length;i++) {
17574 //            editor.toolbars[i] = Roo.factory(
17575 //                    typeof(editor.toolbars[i]) == 'string' ?
17576 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
17577 //                Roo.bootstrap.HtmlEditor);
17578 //            editor.toolbars[i].init(editor);
17579 //        }
17580     },
17581
17582      
17583     // private
17584     onRender : function(ct, position)
17585     {
17586        // Roo.log("Call onRender: " + this.xtype);
17587         var _t = this;
17588         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
17589       
17590         this.wrap = this.inputEl().wrap({
17591             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
17592         });
17593         
17594         this.editorcore.onRender(ct, position);
17595          
17596         if (this.resizable) {
17597             this.resizeEl = new Roo.Resizable(this.wrap, {
17598                 pinned : true,
17599                 wrap: true,
17600                 dynamic : true,
17601                 minHeight : this.height,
17602                 height: this.height,
17603                 handles : this.resizable,
17604                 width: this.width,
17605                 listeners : {
17606                     resize : function(r, w, h) {
17607                         _t.onResize(w,h); // -something
17608                     }
17609                 }
17610             });
17611             
17612         }
17613         this.createToolbar(this);
17614        
17615         
17616         if(!this.width && this.resizable){
17617             this.setSize(this.wrap.getSize());
17618         }
17619         if (this.resizeEl) {
17620             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
17621             // should trigger onReize..
17622         }
17623         
17624     },
17625
17626     // private
17627     onResize : function(w, h)
17628     {
17629         Roo.log('resize: ' +w + ',' + h );
17630         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
17631         var ew = false;
17632         var eh = false;
17633         
17634         if(this.inputEl() ){
17635             if(typeof w == 'number'){
17636                 var aw = w - this.wrap.getFrameWidth('lr');
17637                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
17638                 ew = aw;
17639             }
17640             if(typeof h == 'number'){
17641                  var tbh = -11;  // fixme it needs to tool bar size!
17642                 for (var i =0; i < this.toolbars.length;i++) {
17643                     // fixme - ask toolbars for heights?
17644                     tbh += this.toolbars[i].el.getHeight();
17645                     //if (this.toolbars[i].footer) {
17646                     //    tbh += this.toolbars[i].footer.el.getHeight();
17647                     //}
17648                 }
17649               
17650                 
17651                 
17652                 
17653                 
17654                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
17655                 ah -= 5; // knock a few pixes off for look..
17656                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
17657                 var eh = ah;
17658             }
17659         }
17660         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
17661         this.editorcore.onResize(ew,eh);
17662         
17663     },
17664
17665     /**
17666      * Toggles the editor between standard and source edit mode.
17667      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
17668      */
17669     toggleSourceEdit : function(sourceEditMode)
17670     {
17671         this.editorcore.toggleSourceEdit(sourceEditMode);
17672         
17673         if(this.editorcore.sourceEditMode){
17674             Roo.log('editor - showing textarea');
17675             
17676 //            Roo.log('in');
17677 //            Roo.log(this.syncValue());
17678             this.syncValue();
17679             this.inputEl().removeClass(['hide', 'x-hidden']);
17680             this.inputEl().dom.removeAttribute('tabIndex');
17681             this.inputEl().focus();
17682         }else{
17683             Roo.log('editor - hiding textarea');
17684 //            Roo.log('out')
17685 //            Roo.log(this.pushValue()); 
17686             this.pushValue();
17687             
17688             this.inputEl().addClass(['hide', 'x-hidden']);
17689             this.inputEl().dom.setAttribute('tabIndex', -1);
17690             //this.deferFocus();
17691         }
17692          
17693         if(this.resizable){
17694             this.setSize(this.wrap.getSize());
17695         }
17696         
17697         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
17698     },
17699  
17700     // private (for BoxComponent)
17701     adjustSize : Roo.BoxComponent.prototype.adjustSize,
17702
17703     // private (for BoxComponent)
17704     getResizeEl : function(){
17705         return this.wrap;
17706     },
17707
17708     // private (for BoxComponent)
17709     getPositionEl : function(){
17710         return this.wrap;
17711     },
17712
17713     // private
17714     initEvents : function(){
17715         this.originalValue = this.getValue();
17716     },
17717
17718 //    /**
17719 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
17720 //     * @method
17721 //     */
17722 //    markInvalid : Roo.emptyFn,
17723 //    /**
17724 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
17725 //     * @method
17726 //     */
17727 //    clearInvalid : Roo.emptyFn,
17728
17729     setValue : function(v){
17730         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
17731         this.editorcore.pushValue();
17732     },
17733
17734      
17735     // private
17736     deferFocus : function(){
17737         this.focus.defer(10, this);
17738     },
17739
17740     // doc'ed in Field
17741     focus : function(){
17742         this.editorcore.focus();
17743         
17744     },
17745       
17746
17747     // private
17748     onDestroy : function(){
17749         
17750         
17751         
17752         if(this.rendered){
17753             
17754             for (var i =0; i < this.toolbars.length;i++) {
17755                 // fixme - ask toolbars for heights?
17756                 this.toolbars[i].onDestroy();
17757             }
17758             
17759             this.wrap.dom.innerHTML = '';
17760             this.wrap.remove();
17761         }
17762     },
17763
17764     // private
17765     onFirstFocus : function(){
17766         //Roo.log("onFirstFocus");
17767         this.editorcore.onFirstFocus();
17768          for (var i =0; i < this.toolbars.length;i++) {
17769             this.toolbars[i].onFirstFocus();
17770         }
17771         
17772     },
17773     
17774     // private
17775     syncValue : function()
17776     {   
17777         this.editorcore.syncValue();
17778     },
17779     
17780     pushValue : function()
17781     {   
17782         this.editorcore.pushValue();
17783     }
17784      
17785     
17786     // hide stuff that is not compatible
17787     /**
17788      * @event blur
17789      * @hide
17790      */
17791     /**
17792      * @event change
17793      * @hide
17794      */
17795     /**
17796      * @event focus
17797      * @hide
17798      */
17799     /**
17800      * @event specialkey
17801      * @hide
17802      */
17803     /**
17804      * @cfg {String} fieldClass @hide
17805      */
17806     /**
17807      * @cfg {String} focusClass @hide
17808      */
17809     /**
17810      * @cfg {String} autoCreate @hide
17811      */
17812     /**
17813      * @cfg {String} inputType @hide
17814      */
17815     /**
17816      * @cfg {String} invalidClass @hide
17817      */
17818     /**
17819      * @cfg {String} invalidText @hide
17820      */
17821     /**
17822      * @cfg {String} msgFx @hide
17823      */
17824     /**
17825      * @cfg {String} validateOnBlur @hide
17826      */
17827 });
17828  
17829     
17830    
17831    
17832    
17833       
17834 Roo.namespace('Roo.bootstrap.htmleditor');
17835 /**
17836  * @class Roo.bootstrap.HtmlEditorToolbar1
17837  * Basic Toolbar
17838  * 
17839  * Usage:
17840  *
17841  new Roo.bootstrap.HtmlEditor({
17842     ....
17843     toolbars : [
17844         new Roo.bootstrap.HtmlEditorToolbar1({
17845             disable : { fonts: 1 , format: 1, ..., ... , ...],
17846             btns : [ .... ]
17847         })
17848     }
17849      
17850  * 
17851  * @cfg {Object} disable List of elements to disable..
17852  * @cfg {Array} btns List of additional buttons.
17853  * 
17854  * 
17855  * NEEDS Extra CSS? 
17856  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
17857  */
17858  
17859 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
17860 {
17861     
17862     Roo.apply(this, config);
17863     
17864     // default disabled, based on 'good practice'..
17865     this.disable = this.disable || {};
17866     Roo.applyIf(this.disable, {
17867         fontSize : true,
17868         colors : true,
17869         specialElements : true
17870     });
17871     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
17872     
17873     this.editor = config.editor;
17874     this.editorcore = config.editor.editorcore;
17875     
17876     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
17877     
17878     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
17879     // dont call parent... till later.
17880 }
17881 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
17882      
17883     bar : true,
17884     
17885     editor : false,
17886     editorcore : false,
17887     
17888     
17889     formats : [
17890         "p" ,  
17891         "h1","h2","h3","h4","h5","h6", 
17892         "pre", "code", 
17893         "abbr", "acronym", "address", "cite", "samp", "var",
17894         'div','span'
17895     ],
17896     
17897     onRender : function(ct, position)
17898     {
17899        // Roo.log("Call onRender: " + this.xtype);
17900         
17901        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
17902        Roo.log(this.el);
17903        this.el.dom.style.marginBottom = '0';
17904        var _this = this;
17905        var editorcore = this.editorcore;
17906        var editor= this.editor;
17907        
17908        var children = [];
17909        var btn = function(id,cmd , toggle, handler){
17910        
17911             var  event = toggle ? 'toggle' : 'click';
17912        
17913             var a = {
17914                 size : 'sm',
17915                 xtype: 'Button',
17916                 xns: Roo.bootstrap,
17917                 glyphicon : id,
17918                 cmd : id || cmd,
17919                 enableToggle:toggle !== false,
17920                 //html : 'submit'
17921                 pressed : toggle ? false : null,
17922                 listeners : {}
17923             }
17924             a.listeners[toggle ? 'toggle' : 'click'] = function() {
17925                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
17926             }
17927             children.push(a);
17928             return a;
17929        }
17930         
17931         var style = {
17932                 xtype: 'Button',
17933                 size : 'sm',
17934                 xns: Roo.bootstrap,
17935                 glyphicon : 'font',
17936                 //html : 'submit'
17937                 menu : {
17938                     xtype: 'Menu',
17939                     xns: Roo.bootstrap,
17940                     items:  []
17941                 }
17942         };
17943         Roo.each(this.formats, function(f) {
17944             style.menu.items.push({
17945                 xtype :'MenuItem',
17946                 xns: Roo.bootstrap,
17947                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
17948                 tagname : f,
17949                 listeners : {
17950                     click : function()
17951                     {
17952                         editorcore.insertTag(this.tagname);
17953                         editor.focus();
17954                     }
17955                 }
17956                 
17957             });
17958         });
17959          children.push(style);   
17960             
17961             
17962         btn('bold',false,true);
17963         btn('italic',false,true);
17964         btn('align-left', 'justifyleft',true);
17965         btn('align-center', 'justifycenter',true);
17966         btn('align-right' , 'justifyright',true);
17967         btn('link', false, false, function(btn) {
17968             //Roo.log("create link?");
17969             var url = prompt(this.createLinkText, this.defaultLinkValue);
17970             if(url && url != 'http:/'+'/'){
17971                 this.editorcore.relayCmd('createlink', url);
17972             }
17973         }),
17974         btn('list','insertunorderedlist',true);
17975         btn('pencil', false,true, function(btn){
17976                 Roo.log(this);
17977                 
17978                 this.toggleSourceEdit(btn.pressed);
17979         });
17980         /*
17981         var cog = {
17982                 xtype: 'Button',
17983                 size : 'sm',
17984                 xns: Roo.bootstrap,
17985                 glyphicon : 'cog',
17986                 //html : 'submit'
17987                 menu : {
17988                     xtype: 'Menu',
17989                     xns: Roo.bootstrap,
17990                     items:  []
17991                 }
17992         };
17993         
17994         cog.menu.items.push({
17995             xtype :'MenuItem',
17996             xns: Roo.bootstrap,
17997             html : Clean styles,
17998             tagname : f,
17999             listeners : {
18000                 click : function()
18001                 {
18002                     editorcore.insertTag(this.tagname);
18003                     editor.focus();
18004                 }
18005             }
18006             
18007         });
18008        */
18009         
18010          
18011        this.xtype = 'NavSimplebar';
18012         
18013         for(var i=0;i< children.length;i++) {
18014             
18015             this.buttons.add(this.addxtypeChild(children[i]));
18016             
18017         }
18018         
18019         editor.on('editorevent', this.updateToolbar, this);
18020     },
18021     onBtnClick : function(id)
18022     {
18023        this.editorcore.relayCmd(id);
18024        this.editorcore.focus();
18025     },
18026     
18027     /**
18028      * Protected method that will not generally be called directly. It triggers
18029      * a toolbar update by reading the markup state of the current selection in the editor.
18030      */
18031     updateToolbar: function(){
18032
18033         if(!this.editorcore.activated){
18034             this.editor.onFirstFocus(); // is this neeed?
18035             return;
18036         }
18037
18038         var btns = this.buttons; 
18039         var doc = this.editorcore.doc;
18040         btns.get('bold').setActive(doc.queryCommandState('bold'));
18041         btns.get('italic').setActive(doc.queryCommandState('italic'));
18042         //btns.get('underline').setActive(doc.queryCommandState('underline'));
18043         
18044         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
18045         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
18046         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
18047         
18048         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
18049         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
18050          /*
18051         
18052         var ans = this.editorcore.getAllAncestors();
18053         if (this.formatCombo) {
18054             
18055             
18056             var store = this.formatCombo.store;
18057             this.formatCombo.setValue("");
18058             for (var i =0; i < ans.length;i++) {
18059                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
18060                     // select it..
18061                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
18062                     break;
18063                 }
18064             }
18065         }
18066         
18067         
18068         
18069         // hides menus... - so this cant be on a menu...
18070         Roo.bootstrap.MenuMgr.hideAll();
18071         */
18072         Roo.bootstrap.MenuMgr.hideAll();
18073         //this.editorsyncValue();
18074     },
18075     onFirstFocus: function() {
18076         this.buttons.each(function(item){
18077            item.enable();
18078         });
18079     },
18080     toggleSourceEdit : function(sourceEditMode){
18081         
18082           
18083         if(sourceEditMode){
18084             Roo.log("disabling buttons");
18085            this.buttons.each( function(item){
18086                 if(item.cmd != 'pencil'){
18087                     item.disable();
18088                 }
18089             });
18090           
18091         }else{
18092             Roo.log("enabling buttons");
18093             if(this.editorcore.initialized){
18094                 this.buttons.each( function(item){
18095                     item.enable();
18096                 });
18097             }
18098             
18099         }
18100         Roo.log("calling toggole on editor");
18101         // tell the editor that it's been pressed..
18102         this.editor.toggleSourceEdit(sourceEditMode);
18103        
18104     }
18105 });
18106
18107
18108
18109
18110
18111 /**
18112  * @class Roo.bootstrap.Table.AbstractSelectionModel
18113  * @extends Roo.util.Observable
18114  * Abstract base class for grid SelectionModels.  It provides the interface that should be
18115  * implemented by descendant classes.  This class should not be directly instantiated.
18116  * @constructor
18117  */
18118 Roo.bootstrap.Table.AbstractSelectionModel = function(){
18119     this.locked = false;
18120     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
18121 };
18122
18123
18124 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
18125     /** @ignore Called by the grid automatically. Do not call directly. */
18126     init : function(grid){
18127         this.grid = grid;
18128         this.initEvents();
18129     },
18130
18131     /**
18132      * Locks the selections.
18133      */
18134     lock : function(){
18135         this.locked = true;
18136     },
18137
18138     /**
18139      * Unlocks the selections.
18140      */
18141     unlock : function(){
18142         this.locked = false;
18143     },
18144
18145     /**
18146      * Returns true if the selections are locked.
18147      * @return {Boolean}
18148      */
18149     isLocked : function(){
18150         return this.locked;
18151     }
18152 });
18153 /**
18154  * @extends Roo.bootstrap.Table.AbstractSelectionModel
18155  * @class Roo.bootstrap.Table.RowSelectionModel
18156  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
18157  * It supports multiple selections and keyboard selection/navigation. 
18158  * @constructor
18159  * @param {Object} config
18160  */
18161
18162 Roo.bootstrap.Table.RowSelectionModel = function(config){
18163     Roo.apply(this, config);
18164     this.selections = new Roo.util.MixedCollection(false, function(o){
18165         return o.id;
18166     });
18167
18168     this.last = false;
18169     this.lastActive = false;
18170
18171     this.addEvents({
18172         /**
18173              * @event selectionchange
18174              * Fires when the selection changes
18175              * @param {SelectionModel} this
18176              */
18177             "selectionchange" : true,
18178         /**
18179              * @event afterselectionchange
18180              * Fires after the selection changes (eg. by key press or clicking)
18181              * @param {SelectionModel} this
18182              */
18183             "afterselectionchange" : true,
18184         /**
18185              * @event beforerowselect
18186              * Fires when a row is selected being selected, return false to cancel.
18187              * @param {SelectionModel} this
18188              * @param {Number} rowIndex The selected index
18189              * @param {Boolean} keepExisting False if other selections will be cleared
18190              */
18191             "beforerowselect" : true,
18192         /**
18193              * @event rowselect
18194              * Fires when a row is selected.
18195              * @param {SelectionModel} this
18196              * @param {Number} rowIndex The selected index
18197              * @param {Roo.data.Record} r The record
18198              */
18199             "rowselect" : true,
18200         /**
18201              * @event rowdeselect
18202              * Fires when a row is deselected.
18203              * @param {SelectionModel} this
18204              * @param {Number} rowIndex The selected index
18205              */
18206         "rowdeselect" : true
18207     });
18208     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
18209     this.locked = false;
18210 };
18211
18212 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
18213     /**
18214      * @cfg {Boolean} singleSelect
18215      * True to allow selection of only one row at a time (defaults to false)
18216      */
18217     singleSelect : false,
18218
18219     // private
18220     initEvents : function(){
18221
18222         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
18223             this.grid.on("mousedown", this.handleMouseDown, this);
18224         }else{ // allow click to work like normal
18225             this.grid.on("rowclick", this.handleDragableRowClick, this);
18226         }
18227
18228         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
18229             "up" : function(e){
18230                 if(!e.shiftKey){
18231                     this.selectPrevious(e.shiftKey);
18232                 }else if(this.last !== false && this.lastActive !== false){
18233                     var last = this.last;
18234                     this.selectRange(this.last,  this.lastActive-1);
18235                     this.grid.getView().focusRow(this.lastActive);
18236                     if(last !== false){
18237                         this.last = last;
18238                     }
18239                 }else{
18240                     this.selectFirstRow();
18241                 }
18242                 this.fireEvent("afterselectionchange", this);
18243             },
18244             "down" : function(e){
18245                 if(!e.shiftKey){
18246                     this.selectNext(e.shiftKey);
18247                 }else if(this.last !== false && this.lastActive !== false){
18248                     var last = this.last;
18249                     this.selectRange(this.last,  this.lastActive+1);
18250                     this.grid.getView().focusRow(this.lastActive);
18251                     if(last !== false){
18252                         this.last = last;
18253                     }
18254                 }else{
18255                     this.selectFirstRow();
18256                 }
18257                 this.fireEvent("afterselectionchange", this);
18258             },
18259             scope: this
18260         });
18261
18262         var view = this.grid.view;
18263         view.on("refresh", this.onRefresh, this);
18264         view.on("rowupdated", this.onRowUpdated, this);
18265         view.on("rowremoved", this.onRemove, this);
18266     },
18267
18268     // private
18269     onRefresh : function(){
18270         var ds = this.grid.dataSource, i, v = this.grid.view;
18271         var s = this.selections;
18272         s.each(function(r){
18273             if((i = ds.indexOfId(r.id)) != -1){
18274                 v.onRowSelect(i);
18275             }else{
18276                 s.remove(r);
18277             }
18278         });
18279     },
18280
18281     // private
18282     onRemove : function(v, index, r){
18283         this.selections.remove(r);
18284     },
18285
18286     // private
18287     onRowUpdated : function(v, index, r){
18288         if(this.isSelected(r)){
18289             v.onRowSelect(index);
18290         }
18291     },
18292
18293     /**
18294      * Select records.
18295      * @param {Array} records The records to select
18296      * @param {Boolean} keepExisting (optional) True to keep existing selections
18297      */
18298     selectRecords : function(records, keepExisting){
18299         if(!keepExisting){
18300             this.clearSelections();
18301         }
18302         var ds = this.grid.dataSource;
18303         for(var i = 0, len = records.length; i < len; i++){
18304             this.selectRow(ds.indexOf(records[i]), true);
18305         }
18306     },
18307
18308     /**
18309      * Gets the number of selected rows.
18310      * @return {Number}
18311      */
18312     getCount : function(){
18313         return this.selections.length;
18314     },
18315
18316     /**
18317      * Selects the first row in the grid.
18318      */
18319     selectFirstRow : function(){
18320         this.selectRow(0);
18321     },
18322
18323     /**
18324      * Select the last row.
18325      * @param {Boolean} keepExisting (optional) True to keep existing selections
18326      */
18327     selectLastRow : function(keepExisting){
18328         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
18329     },
18330
18331     /**
18332      * Selects the row immediately following the last selected row.
18333      * @param {Boolean} keepExisting (optional) True to keep existing selections
18334      */
18335     selectNext : function(keepExisting){
18336         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
18337             this.selectRow(this.last+1, keepExisting);
18338             this.grid.getView().focusRow(this.last);
18339         }
18340     },
18341
18342     /**
18343      * Selects the row that precedes the last selected row.
18344      * @param {Boolean} keepExisting (optional) True to keep existing selections
18345      */
18346     selectPrevious : function(keepExisting){
18347         if(this.last){
18348             this.selectRow(this.last-1, keepExisting);
18349             this.grid.getView().focusRow(this.last);
18350         }
18351     },
18352
18353     /**
18354      * Returns the selected records
18355      * @return {Array} Array of selected records
18356      */
18357     getSelections : function(){
18358         return [].concat(this.selections.items);
18359     },
18360
18361     /**
18362      * Returns the first selected record.
18363      * @return {Record}
18364      */
18365     getSelected : function(){
18366         return this.selections.itemAt(0);
18367     },
18368
18369
18370     /**
18371      * Clears all selections.
18372      */
18373     clearSelections : function(fast){
18374         if(this.locked) return;
18375         if(fast !== true){
18376             var ds = this.grid.dataSource;
18377             var s = this.selections;
18378             s.each(function(r){
18379                 this.deselectRow(ds.indexOfId(r.id));
18380             }, this);
18381             s.clear();
18382         }else{
18383             this.selections.clear();
18384         }
18385         this.last = false;
18386     },
18387
18388
18389     /**
18390      * Selects all rows.
18391      */
18392     selectAll : function(){
18393         if(this.locked) return;
18394         this.selections.clear();
18395         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
18396             this.selectRow(i, true);
18397         }
18398     },
18399
18400     /**
18401      * Returns True if there is a selection.
18402      * @return {Boolean}
18403      */
18404     hasSelection : function(){
18405         return this.selections.length > 0;
18406     },
18407
18408     /**
18409      * Returns True if the specified row is selected.
18410      * @param {Number/Record} record The record or index of the record to check
18411      * @return {Boolean}
18412      */
18413     isSelected : function(index){
18414         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
18415         return (r && this.selections.key(r.id) ? true : false);
18416     },
18417
18418     /**
18419      * Returns True if the specified record id is selected.
18420      * @param {String} id The id of record to check
18421      * @return {Boolean}
18422      */
18423     isIdSelected : function(id){
18424         return (this.selections.key(id) ? true : false);
18425     },
18426
18427     // private
18428     handleMouseDown : function(e, t){
18429         var view = this.grid.getView(), rowIndex;
18430         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
18431             return;
18432         };
18433         if(e.shiftKey && this.last !== false){
18434             var last = this.last;
18435             this.selectRange(last, rowIndex, e.ctrlKey);
18436             this.last = last; // reset the last
18437             view.focusRow(rowIndex);
18438         }else{
18439             var isSelected = this.isSelected(rowIndex);
18440             if(e.button !== 0 && isSelected){
18441                 view.focusRow(rowIndex);
18442             }else if(e.ctrlKey && isSelected){
18443                 this.deselectRow(rowIndex);
18444             }else if(!isSelected){
18445                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
18446                 view.focusRow(rowIndex);
18447             }
18448         }
18449         this.fireEvent("afterselectionchange", this);
18450     },
18451     // private
18452     handleDragableRowClick :  function(grid, rowIndex, e) 
18453     {
18454         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
18455             this.selectRow(rowIndex, false);
18456             grid.view.focusRow(rowIndex);
18457              this.fireEvent("afterselectionchange", this);
18458         }
18459     },
18460     
18461     /**
18462      * Selects multiple rows.
18463      * @param {Array} rows Array of the indexes of the row to select
18464      * @param {Boolean} keepExisting (optional) True to keep existing selections
18465      */
18466     selectRows : function(rows, keepExisting){
18467         if(!keepExisting){
18468             this.clearSelections();
18469         }
18470         for(var i = 0, len = rows.length; i < len; i++){
18471             this.selectRow(rows[i], true);
18472         }
18473     },
18474
18475     /**
18476      * Selects a range of rows. All rows in between startRow and endRow are also selected.
18477      * @param {Number} startRow The index of the first row in the range
18478      * @param {Number} endRow The index of the last row in the range
18479      * @param {Boolean} keepExisting (optional) True to retain existing selections
18480      */
18481     selectRange : function(startRow, endRow, keepExisting){
18482         if(this.locked) return;
18483         if(!keepExisting){
18484             this.clearSelections();
18485         }
18486         if(startRow <= endRow){
18487             for(var i = startRow; i <= endRow; i++){
18488                 this.selectRow(i, true);
18489             }
18490         }else{
18491             for(var i = startRow; i >= endRow; i--){
18492                 this.selectRow(i, true);
18493             }
18494         }
18495     },
18496
18497     /**
18498      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
18499      * @param {Number} startRow The index of the first row in the range
18500      * @param {Number} endRow The index of the last row in the range
18501      */
18502     deselectRange : function(startRow, endRow, preventViewNotify){
18503         if(this.locked) return;
18504         for(var i = startRow; i <= endRow; i++){
18505             this.deselectRow(i, preventViewNotify);
18506         }
18507     },
18508
18509     /**
18510      * Selects a row.
18511      * @param {Number} row The index of the row to select
18512      * @param {Boolean} keepExisting (optional) True to keep existing selections
18513      */
18514     selectRow : function(index, keepExisting, preventViewNotify){
18515         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
18516         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
18517             if(!keepExisting || this.singleSelect){
18518                 this.clearSelections();
18519             }
18520             var r = this.grid.dataSource.getAt(index);
18521             this.selections.add(r);
18522             this.last = this.lastActive = index;
18523             if(!preventViewNotify){
18524                 this.grid.getView().onRowSelect(index);
18525             }
18526             this.fireEvent("rowselect", this, index, r);
18527             this.fireEvent("selectionchange", this);
18528         }
18529     },
18530
18531     /**
18532      * Deselects a row.
18533      * @param {Number} row The index of the row to deselect
18534      */
18535     deselectRow : function(index, preventViewNotify){
18536         if(this.locked) return;
18537         if(this.last == index){
18538             this.last = false;
18539         }
18540         if(this.lastActive == index){
18541             this.lastActive = false;
18542         }
18543         var r = this.grid.dataSource.getAt(index);
18544         this.selections.remove(r);
18545         if(!preventViewNotify){
18546             this.grid.getView().onRowDeselect(index);
18547         }
18548         this.fireEvent("rowdeselect", this, index);
18549         this.fireEvent("selectionchange", this);
18550     },
18551
18552     // private
18553     restoreLast : function(){
18554         if(this._last){
18555             this.last = this._last;
18556         }
18557     },
18558
18559     // private
18560     acceptsNav : function(row, col, cm){
18561         return !cm.isHidden(col) && cm.isCellEditable(col, row);
18562     },
18563
18564     // private
18565     onEditorKey : function(field, e){
18566         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
18567         if(k == e.TAB){
18568             e.stopEvent();
18569             ed.completeEdit();
18570             if(e.shiftKey){
18571                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
18572             }else{
18573                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
18574             }
18575         }else if(k == e.ENTER && !e.ctrlKey){
18576             e.stopEvent();
18577             ed.completeEdit();
18578             if(e.shiftKey){
18579                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
18580             }else{
18581                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
18582             }
18583         }else if(k == e.ESC){
18584             ed.cancelEdit();
18585         }
18586         if(newCell){
18587             g.startEditing(newCell[0], newCell[1]);
18588         }
18589     }
18590 });/*
18591  * Based on:
18592  * Ext JS Library 1.1.1
18593  * Copyright(c) 2006-2007, Ext JS, LLC.
18594  *
18595  * Originally Released Under LGPL - original licence link has changed is not relivant.
18596  *
18597  * Fork - LGPL
18598  * <script type="text/javascript">
18599  */
18600  
18601 /**
18602  * @class Roo.bootstrap.PagingToolbar
18603  * @extends Roo.Row
18604  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
18605  * @constructor
18606  * Create a new PagingToolbar
18607  * @param {Object} config The config object
18608  */
18609 Roo.bootstrap.PagingToolbar = function(config)
18610 {
18611     // old args format still supported... - xtype is prefered..
18612         // created from xtype...
18613     var ds = config.dataSource;
18614     this.toolbarItems = [];
18615     if (config.items) {
18616         this.toolbarItems = config.items;
18617 //        config.items = [];
18618     }
18619     
18620     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
18621     this.ds = ds;
18622     this.cursor = 0;
18623     if (ds) { 
18624         this.bind(ds);
18625     }
18626     
18627     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
18628     
18629 };
18630
18631 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
18632     /**
18633      * @cfg {Roo.data.Store} dataSource
18634      * The underlying data store providing the paged data
18635      */
18636     /**
18637      * @cfg {String/HTMLElement/Element} container
18638      * container The id or element that will contain the toolbar
18639      */
18640     /**
18641      * @cfg {Boolean} displayInfo
18642      * True to display the displayMsg (defaults to false)
18643      */
18644     /**
18645      * @cfg {Number} pageSize
18646      * The number of records to display per page (defaults to 20)
18647      */
18648     pageSize: 20,
18649     /**
18650      * @cfg {String} displayMsg
18651      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
18652      */
18653     displayMsg : 'Displaying {0} - {1} of {2}',
18654     /**
18655      * @cfg {String} emptyMsg
18656      * The message to display when no records are found (defaults to "No data to display")
18657      */
18658     emptyMsg : 'No data to display',
18659     /**
18660      * Customizable piece of the default paging text (defaults to "Page")
18661      * @type String
18662      */
18663     beforePageText : "Page",
18664     /**
18665      * Customizable piece of the default paging text (defaults to "of %0")
18666      * @type String
18667      */
18668     afterPageText : "of {0}",
18669     /**
18670      * Customizable piece of the default paging text (defaults to "First Page")
18671      * @type String
18672      */
18673     firstText : "First Page",
18674     /**
18675      * Customizable piece of the default paging text (defaults to "Previous Page")
18676      * @type String
18677      */
18678     prevText : "Previous Page",
18679     /**
18680      * Customizable piece of the default paging text (defaults to "Next Page")
18681      * @type String
18682      */
18683     nextText : "Next Page",
18684     /**
18685      * Customizable piece of the default paging text (defaults to "Last Page")
18686      * @type String
18687      */
18688     lastText : "Last Page",
18689     /**
18690      * Customizable piece of the default paging text (defaults to "Refresh")
18691      * @type String
18692      */
18693     refreshText : "Refresh",
18694
18695     buttons : false,
18696     // private
18697     onRender : function(ct, position) 
18698     {
18699         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
18700         this.navgroup.parentId = this.id;
18701         this.navgroup.onRender(this.el, null);
18702         // add the buttons to the navgroup
18703         
18704         if(this.displayInfo){
18705             Roo.log(this.el.select('ul.navbar-nav',true).first());
18706             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
18707             this.displayEl = this.el.select('.x-paging-info', true).first();
18708 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
18709 //            this.displayEl = navel.el.select('span',true).first();
18710         }
18711         
18712         var _this = this;
18713         
18714         if(this.buttons){
18715             Roo.each(_this.buttons, function(e){
18716                Roo.factory(e).onRender(_this.el, null);
18717             });
18718         }
18719             
18720         Roo.each(_this.toolbarItems, function(e) {
18721             _this.navgroup.addItem(e);
18722         });
18723         
18724         this.first = this.navgroup.addItem({
18725             tooltip: this.firstText,
18726             cls: "prev",
18727             icon : 'fa fa-backward',
18728             disabled: true,
18729             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
18730         });
18731         
18732         this.prev =  this.navgroup.addItem({
18733             tooltip: this.prevText,
18734             cls: "prev",
18735             icon : 'fa fa-step-backward',
18736             disabled: true,
18737             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
18738         });
18739     //this.addSeparator();
18740         
18741         
18742         var field = this.navgroup.addItem( {
18743             tagtype : 'span',
18744             cls : 'x-paging-position',
18745             
18746             html : this.beforePageText  +
18747                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
18748                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
18749          } ); //?? escaped?
18750         
18751         this.field = field.el.select('input', true).first();
18752         this.field.on("keydown", this.onPagingKeydown, this);
18753         this.field.on("focus", function(){this.dom.select();});
18754     
18755     
18756         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
18757         //this.field.setHeight(18);
18758         //this.addSeparator();
18759         this.next = this.navgroup.addItem({
18760             tooltip: this.nextText,
18761             cls: "next",
18762             html : ' <i class="fa fa-step-forward">',
18763             disabled: true,
18764             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
18765         });
18766         this.last = this.navgroup.addItem({
18767             tooltip: this.lastText,
18768             icon : 'fa fa-forward',
18769             cls: "next",
18770             disabled: true,
18771             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
18772         });
18773     //this.addSeparator();
18774         this.loading = this.navgroup.addItem({
18775             tooltip: this.refreshText,
18776             icon: 'fa fa-refresh',
18777             
18778             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
18779         });
18780
18781     },
18782
18783     // private
18784     updateInfo : function(){
18785         if(this.displayEl){
18786             var count = this.ds.getCount();
18787             var msg = count == 0 ?
18788                 this.emptyMsg :
18789                 String.format(
18790                     this.displayMsg,
18791                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
18792                 );
18793             this.displayEl.update(msg);
18794         }
18795     },
18796
18797     // private
18798     onLoad : function(ds, r, o){
18799        this.cursor = o.params ? o.params.start : 0;
18800        var d = this.getPageData(),
18801             ap = d.activePage,
18802             ps = d.pages;
18803         
18804        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
18805        this.field.dom.value = ap;
18806        this.first.setDisabled(ap == 1);
18807        this.prev.setDisabled(ap == 1);
18808        this.next.setDisabled(ap == ps);
18809        this.last.setDisabled(ap == ps);
18810        this.loading.enable();
18811        this.updateInfo();
18812     },
18813
18814     // private
18815     getPageData : function(){
18816         var total = this.ds.getTotalCount();
18817         return {
18818             total : total,
18819             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
18820             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
18821         };
18822     },
18823
18824     // private
18825     onLoadError : function(){
18826         this.loading.enable();
18827     },
18828
18829     // private
18830     onPagingKeydown : function(e){
18831         var k = e.getKey();
18832         var d = this.getPageData();
18833         if(k == e.RETURN){
18834             var v = this.field.dom.value, pageNum;
18835             if(!v || isNaN(pageNum = parseInt(v, 10))){
18836                 this.field.dom.value = d.activePage;
18837                 return;
18838             }
18839             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
18840             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
18841             e.stopEvent();
18842         }
18843         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))
18844         {
18845           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
18846           this.field.dom.value = pageNum;
18847           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
18848           e.stopEvent();
18849         }
18850         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
18851         {
18852           var v = this.field.dom.value, pageNum; 
18853           var increment = (e.shiftKey) ? 10 : 1;
18854           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
18855             increment *= -1;
18856           if(!v || isNaN(pageNum = parseInt(v, 10))) {
18857             this.field.dom.value = d.activePage;
18858             return;
18859           }
18860           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
18861           {
18862             this.field.dom.value = parseInt(v, 10) + increment;
18863             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
18864             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
18865           }
18866           e.stopEvent();
18867         }
18868     },
18869
18870     // private
18871     beforeLoad : function(){
18872         if(this.loading){
18873             this.loading.disable();
18874         }
18875     },
18876
18877     // private
18878     onClick : function(which){
18879         var ds = this.ds;
18880         if (!ds) {
18881             return;
18882         }
18883         switch(which){
18884             case "first":
18885                 ds.load({params:{start: 0, limit: this.pageSize}});
18886             break;
18887             case "prev":
18888                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
18889             break;
18890             case "next":
18891                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
18892             break;
18893             case "last":
18894                 var total = ds.getTotalCount();
18895                 var extra = total % this.pageSize;
18896                 var lastStart = extra ? (total - extra) : total-this.pageSize;
18897                 ds.load({params:{start: lastStart, limit: this.pageSize}});
18898             break;
18899             case "refresh":
18900                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
18901             break;
18902         }
18903     },
18904
18905     /**
18906      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
18907      * @param {Roo.data.Store} store The data store to unbind
18908      */
18909     unbind : function(ds){
18910         ds.un("beforeload", this.beforeLoad, this);
18911         ds.un("load", this.onLoad, this);
18912         ds.un("loadexception", this.onLoadError, this);
18913         ds.un("remove", this.updateInfo, this);
18914         ds.un("add", this.updateInfo, this);
18915         this.ds = undefined;
18916     },
18917
18918     /**
18919      * Binds the paging toolbar to the specified {@link Roo.data.Store}
18920      * @param {Roo.data.Store} store The data store to bind
18921      */
18922     bind : function(ds){
18923         ds.on("beforeload", this.beforeLoad, this);
18924         ds.on("load", this.onLoad, this);
18925         ds.on("loadexception", this.onLoadError, this);
18926         ds.on("remove", this.updateInfo, this);
18927         ds.on("add", this.updateInfo, this);
18928         this.ds = ds;
18929     }
18930 });/*
18931  * - LGPL
18932  *
18933  * element
18934  * 
18935  */
18936
18937 /**
18938  * @class Roo.bootstrap.MessageBar
18939  * @extends Roo.bootstrap.Component
18940  * Bootstrap MessageBar class
18941  * @cfg {String} html contents of the MessageBar
18942  * @cfg {String} weight (info | success | warning | danger) default info
18943  * @cfg {String} beforeClass insert the bar before the given class
18944  * @cfg {Boolean} closable (true | false) default false
18945  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
18946  * 
18947  * @constructor
18948  * Create a new Element
18949  * @param {Object} config The config object
18950  */
18951
18952 Roo.bootstrap.MessageBar = function(config){
18953     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
18954 };
18955
18956 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
18957     
18958     html: '',
18959     weight: 'info',
18960     closable: false,
18961     fixed: false,
18962     beforeClass: 'bootstrap-sticky-wrap',
18963     
18964     getAutoCreate : function(){
18965         
18966         var cfg = {
18967             tag: 'div',
18968             cls: 'alert alert-dismissable alert-' + this.weight,
18969             cn: [
18970                 {
18971                     tag: 'span',
18972                     cls: 'message',
18973                     html: this.html || ''
18974                 }
18975             ]
18976         }
18977         
18978         if(this.fixed){
18979             cfg.cls += ' alert-messages-fixed';
18980         }
18981         
18982         if(this.closable){
18983             cfg.cn.push({
18984                 tag: 'button',
18985                 cls: 'close',
18986                 html: 'x'
18987             });
18988         }
18989         
18990         return cfg;
18991     },
18992     
18993     onRender : function(ct, position)
18994     {
18995         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
18996         
18997         if(!this.el){
18998             var cfg = Roo.apply({},  this.getAutoCreate());
18999             cfg.id = Roo.id();
19000             
19001             if (this.cls) {
19002                 cfg.cls += ' ' + this.cls;
19003             }
19004             if (this.style) {
19005                 cfg.style = this.style;
19006             }
19007             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
19008             
19009             this.el.setVisibilityMode(Roo.Element.DISPLAY);
19010         }
19011         
19012         this.el.select('>button.close').on('click', this.hide, this);
19013         
19014     },
19015     
19016     show : function()
19017     {
19018         if (!this.rendered) {
19019             this.render();
19020         }
19021         
19022         this.el.show();
19023         
19024         this.fireEvent('show', this);
19025         
19026     },
19027     
19028     hide : function()
19029     {
19030         if (!this.rendered) {
19031             this.render();
19032         }
19033         
19034         this.el.hide();
19035         
19036         this.fireEvent('hide', this);
19037     },
19038     
19039     update : function()
19040     {
19041 //        var e = this.el.dom.firstChild;
19042 //        
19043 //        if(this.closable){
19044 //            e = e.nextSibling;
19045 //        }
19046 //        
19047 //        e.data = this.html || '';
19048
19049         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
19050     }
19051    
19052 });
19053
19054  
19055
19056      /*
19057  * - LGPL
19058  *
19059  * Graph
19060  * 
19061  */
19062
19063
19064 /**
19065  * @class Roo.bootstrap.Graph
19066  * @extends Roo.bootstrap.Component
19067  * Bootstrap Graph class
19068 > Prameters
19069  -sm {number} sm 4
19070  -md {number} md 5
19071  @cfg {String} graphtype  bar | vbar | pie
19072  @cfg {number} g_x coodinator | centre x (pie)
19073  @cfg {number} g_y coodinator | centre y (pie)
19074  @cfg {number} g_r radius (pie)
19075  @cfg {number} g_height height of the chart (respected by all elements in the set)
19076  @cfg {number} g_width width of the chart (respected by all elements in the set)
19077  @cfg {Object} title The title of the chart
19078     
19079  -{Array}  values
19080  -opts (object) options for the chart 
19081      o {
19082      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
19083      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
19084      o vgutter (number)
19085      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.
19086      o stacked (boolean) whether or not to tread values as in a stacked bar chart
19087      o to
19088      o stretch (boolean)
19089      o }
19090  -opts (object) options for the pie
19091      o{
19092      o cut
19093      o startAngle (number)
19094      o endAngle (number)
19095      } 
19096  *
19097  * @constructor
19098  * Create a new Input
19099  * @param {Object} config The config object
19100  */
19101
19102 Roo.bootstrap.Graph = function(config){
19103     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
19104     
19105     this.addEvents({
19106         // img events
19107         /**
19108          * @event click
19109          * The img click event for the img.
19110          * @param {Roo.EventObject} e
19111          */
19112         "click" : true
19113     });
19114 };
19115
19116 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
19117     
19118     sm: 4,
19119     md: 5,
19120     graphtype: 'bar',
19121     g_height: 250,
19122     g_width: 400,
19123     g_x: 50,
19124     g_y: 50,
19125     g_r: 30,
19126     opts:{
19127         //g_colors: this.colors,
19128         g_type: 'soft',
19129         g_gutter: '20%'
19130
19131     },
19132     title : false,
19133
19134     getAutoCreate : function(){
19135         
19136         var cfg = {
19137             tag: 'div',
19138             html : null
19139         }
19140         
19141         
19142         return  cfg;
19143     },
19144
19145     onRender : function(ct,position){
19146         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
19147         this.raphael = Raphael(this.el.dom);
19148         
19149                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19150                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19151                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19152                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
19153                 /*
19154                 r.text(160, 10, "Single Series Chart").attr(txtattr);
19155                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
19156                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
19157                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
19158                 
19159                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
19160                 r.barchart(330, 10, 300, 220, data1);
19161                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
19162                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
19163                 */
19164                 
19165                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19166                 // r.barchart(30, 30, 560, 250,  xdata, {
19167                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
19168                 //     axis : "0 0 1 1",
19169                 //     axisxlabels :  xdata
19170                 //     //yvalues : cols,
19171                    
19172                 // });
19173 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19174 //        
19175 //        this.load(null,xdata,{
19176 //                axis : "0 0 1 1",
19177 //                axisxlabels :  xdata
19178 //                });
19179
19180     },
19181
19182     load : function(graphtype,xdata,opts){
19183         this.raphael.clear();
19184         if(!graphtype) {
19185             graphtype = this.graphtype;
19186         }
19187         if(!opts){
19188             opts = this.opts;
19189         }
19190         var r = this.raphael,
19191             fin = function () {
19192                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
19193             },
19194             fout = function () {
19195                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
19196             },
19197             pfin = function() {
19198                 this.sector.stop();
19199                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
19200
19201                 if (this.label) {
19202                     this.label[0].stop();
19203                     this.label[0].attr({ r: 7.5 });
19204                     this.label[1].attr({ "font-weight": 800 });
19205                 }
19206             },
19207             pfout = function() {
19208                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
19209
19210                 if (this.label) {
19211                     this.label[0].animate({ r: 5 }, 500, "bounce");
19212                     this.label[1].attr({ "font-weight": 400 });
19213                 }
19214             };
19215
19216         switch(graphtype){
19217             case 'bar':
19218                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19219                 break;
19220             case 'hbar':
19221                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19222                 break;
19223             case 'pie':
19224 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
19225 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
19226 //            
19227                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
19228                 
19229                 break;
19230
19231         }
19232         
19233         if(this.title){
19234             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
19235         }
19236         
19237     },
19238     
19239     setTitle: function(o)
19240     {
19241         this.title = o;
19242     },
19243     
19244     initEvents: function() {
19245         
19246         if(!this.href){
19247             this.el.on('click', this.onClick, this);
19248         }
19249     },
19250     
19251     onClick : function(e)
19252     {
19253         Roo.log('img onclick');
19254         this.fireEvent('click', this, e);
19255     }
19256    
19257 });
19258
19259  
19260 /*
19261  * - LGPL
19262  *
19263  * numberBox
19264  * 
19265  */
19266 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19267
19268 /**
19269  * @class Roo.bootstrap.dash.NumberBox
19270  * @extends Roo.bootstrap.Component
19271  * Bootstrap NumberBox class
19272  * @cfg {String} bgcolor Box background color, such as (aqua | green | yellow | red etc..) Default aqua
19273  * @cfg {String} headline Box headline
19274  * @cfg {String} content Box content
19275  * @cfg {String} icon Box icon
19276  * @cfg {String} footer Footer text
19277  * @cfg {String} fhref Footer href
19278  * 
19279  * @constructor
19280  * Create a new NumberBox
19281  * @param {Object} config The config object
19282  */
19283
19284
19285 Roo.bootstrap.dash.NumberBox = function(config){
19286     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
19287     
19288 };
19289
19290 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
19291     
19292     bgcolor : 'aqua',
19293     headline : '',
19294     content : '',
19295     icon : '',
19296     footer : '',
19297     fhref : '',
19298     ficon : '',
19299     
19300     getAutoCreate : function(){
19301         
19302         var cfg = {
19303             tag : 'div',
19304             cls : 'small-box bg-' + this.bgcolor,
19305             cn : [
19306                 {
19307                     tag : 'div',
19308                     cls : 'inner',
19309                     cn :[
19310                         {
19311                             tag : 'h3',
19312                             cls : 'roo-headline',
19313                             html : this.headline
19314                         },
19315                         {
19316                             tag : 'p',
19317                             cls : 'roo-content',
19318                             html : this.content
19319                         }
19320                     ]
19321                 }
19322             ]
19323         }
19324         
19325         if(this.icon){
19326             cfg.cn.push({
19327                 tag : 'div',
19328                 cls : 'icon',
19329                 cn :[
19330                     {
19331                         tag : 'i',
19332                         cls : 'ion ' + this.icon
19333                     }
19334                 ]
19335             });
19336         }
19337         
19338         if(this.footer){
19339             var footer = {
19340                 tag : 'a',
19341                 cls : 'small-box-footer',
19342                 href : this.fhref || '#',
19343                 html : this.footer
19344             };
19345             
19346             cfg.cn.push(footer);
19347             
19348         }
19349         
19350         return  cfg;
19351     },
19352
19353     onRender : function(ct,position){
19354         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
19355
19356
19357        
19358                 
19359     },
19360
19361     setHeadline: function (value)
19362     {
19363         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
19364     },
19365     
19366     setFooter: function (value, href)
19367     {
19368         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
19369         
19370         if(href){
19371             this.el.select('a.small-box-footer',true).first().attr('href', href);
19372         }
19373         
19374     },
19375
19376     setContent: function (value)
19377     {
19378         this.el.select('.roo-content',true).first().dom.innerHTML = value;
19379     },
19380
19381     initEvents: function() 
19382     {   
19383         
19384     }
19385     
19386 });
19387
19388  
19389 /*
19390  * - LGPL
19391  *
19392  * TabBox
19393  * 
19394  */
19395 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19396
19397 /**
19398  * @class Roo.bootstrap.dash.TabBox
19399  * @extends Roo.bootstrap.Component
19400  * Bootstrap TabBox class
19401  * @cfg {String} title Title of the TabBox
19402  * @cfg {String} icon Icon of the TabBox
19403  * 
19404  * @constructor
19405  * Create a new TabBox
19406  * @param {Object} config The config object
19407  */
19408
19409
19410 Roo.bootstrap.dash.TabBox = function(config){
19411     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
19412     this.addEvents({
19413         // raw events
19414         /**
19415          * @event addpane
19416          * When a pane is added
19417          * @param {Roo.bootstrap.dash.TabPane} pane
19418          */
19419         "addpane" : true
19420          
19421     });
19422 };
19423
19424 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
19425
19426     title : '',
19427     icon : false,
19428     
19429     getChildContainer : function()
19430     {
19431         return this.el.select('.tab-content', true).first();
19432     },
19433     
19434     getAutoCreate : function(){
19435         
19436         var header = {
19437             tag: 'li',
19438             cls: 'pull-left header',
19439             html: this.title,
19440             cn : []
19441         };
19442         
19443         if(this.icon){
19444             header.cn.push({
19445                 tag: 'i',
19446                 cls: 'fa ' + this.icon
19447             });
19448         }
19449         
19450         
19451         var cfg = {
19452             tag: 'div',
19453             cls: 'nav-tabs-custom',
19454             cn: [
19455                 {
19456                     tag: 'ul',
19457                     cls: 'nav nav-tabs pull-right',
19458                     cn: [
19459                         header
19460                     ]
19461                 },
19462                 {
19463                     tag: 'div',
19464                     cls: 'tab-content no-padding',
19465                     cn: []
19466                 }
19467             ]
19468         }
19469
19470         return  cfg;
19471     },
19472     initEvents : function()
19473     {
19474         //Roo.log('add add pane handler');
19475         this.on('addpane', this.onAddPane, this);
19476     },
19477      /**
19478      * Updates the box title
19479      * @param {String} html to set the title to.
19480      */
19481     setTitle : function(value)
19482     {
19483         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
19484     },
19485     onAddPane : function(pane)
19486     {
19487         //Roo.log('addpane');
19488         //Roo.log(pane);
19489         // tabs are rendere left to right..
19490         var ctr = this.el.select('.nav-tabs', true).first();
19491          
19492          
19493         var existing = ctr.select('.nav-tab',true);
19494         var qty = existing.getCount();;
19495         
19496         
19497         var tab = ctr.createChild({
19498             tag : 'li',
19499             cls : 'nav-tab' + (qty ? '' : ' active'),
19500             cn : [
19501                 {
19502                     tag : 'a',
19503                     href:'#',
19504                     html : pane.title
19505                 }
19506             ]
19507         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
19508         pane.tab = tab;
19509         
19510         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
19511         if (!qty) {
19512             pane.el.addClass('active');
19513         }
19514         
19515                 
19516     },
19517     onTabClick : function(ev,un,ob,pane)
19518     {
19519         //Roo.log('tab - prev default');
19520         ev.preventDefault();
19521         
19522         
19523         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
19524         pane.tab.addClass('active');
19525         //Roo.log(pane.title);
19526         this.getChildContainer().select('.tab-pane',true).removeClass('active');
19527         // technically we should have a deactivate event.. but maybe add later.
19528         // and it should not de-activate the selected tab...
19529         
19530         pane.el.addClass('active');
19531         pane.fireEvent('activate');
19532         
19533         
19534     }
19535     
19536     
19537 });
19538
19539  
19540 /*
19541  * - LGPL
19542  *
19543  * Tab pane
19544  * 
19545  */
19546 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19547 /**
19548  * @class Roo.bootstrap.TabPane
19549  * @extends Roo.bootstrap.Component
19550  * Bootstrap TabPane class
19551  * @cfg {Boolean} active (false | true) Default false
19552  * @cfg {String} title title of panel
19553
19554  * 
19555  * @constructor
19556  * Create a new TabPane
19557  * @param {Object} config The config object
19558  */
19559
19560 Roo.bootstrap.dash.TabPane = function(config){
19561     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
19562     
19563 };
19564
19565 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
19566     
19567     active : false,
19568     title : '',
19569     
19570     // the tabBox that this is attached to.
19571     tab : false,
19572      
19573     getAutoCreate : function() 
19574     {
19575         var cfg = {
19576             tag: 'div',
19577             cls: 'tab-pane'
19578         }
19579         
19580         if(this.active){
19581             cfg.cls += ' active';
19582         }
19583         
19584         return cfg;
19585     },
19586     initEvents  : function()
19587     {
19588         //Roo.log('trigger add pane handler');
19589         this.parent().fireEvent('addpane', this)
19590     },
19591     
19592      /**
19593      * Updates the tab title 
19594      * @param {String} html to set the title to.
19595      */
19596     setTitle: function(str)
19597     {
19598         if (!this.tab) {
19599             return;
19600         }
19601         this.title = str;
19602         this.tab.select('a'.true).first().dom.innerHTML = str;
19603         
19604     }
19605     
19606     
19607     
19608 });
19609
19610  
19611
19612
19613  /*
19614  * - LGPL
19615  *
19616  * menu
19617  * 
19618  */
19619 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
19620
19621 /**
19622  * @class Roo.bootstrap.menu.Menu
19623  * @extends Roo.bootstrap.Component
19624  * Bootstrap Menu class - container for Menu
19625  * @cfg {String} html Text of the menu
19626  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
19627  * @cfg {String} icon Font awesome icon
19628  * @cfg {String} pos Menu align to (top | bottom) default bottom
19629  * 
19630  * 
19631  * @constructor
19632  * Create a new Menu
19633  * @param {Object} config The config object
19634  */
19635
19636
19637 Roo.bootstrap.menu.Menu = function(config){
19638     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
19639     
19640     this.addEvents({
19641         /**
19642          * @event beforeshow
19643          * Fires before this menu is displayed
19644          * @param {Roo.bootstrap.menu.Menu} this
19645          */
19646         beforeshow : true,
19647         /**
19648          * @event beforehide
19649          * Fires before this menu is hidden
19650          * @param {Roo.bootstrap.menu.Menu} this
19651          */
19652         beforehide : true,
19653         /**
19654          * @event show
19655          * Fires after this menu is displayed
19656          * @param {Roo.bootstrap.menu.Menu} this
19657          */
19658         show : true,
19659         /**
19660          * @event hide
19661          * Fires after this menu is hidden
19662          * @param {Roo.bootstrap.menu.Menu} this
19663          */
19664         hide : true,
19665         /**
19666          * @event click
19667          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19668          * @param {Roo.bootstrap.menu.Menu} this
19669          * @param {Roo.EventObject} e
19670          */
19671         click : true
19672     });
19673     
19674 };
19675
19676 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
19677     
19678     submenu : false,
19679     html : '',
19680     weight : 'default',
19681     icon : false,
19682     pos : 'bottom',
19683     
19684     
19685     getChildContainer : function() {
19686         if(this.isSubMenu){
19687             return this.el;
19688         }
19689         
19690         return this.el.select('ul.dropdown-menu', true).first();  
19691     },
19692     
19693     getAutoCreate : function()
19694     {
19695         var text = [
19696             {
19697                 tag : 'span',
19698                 cls : 'roo-menu-text',
19699                 html : this.html
19700             }
19701         ];
19702         
19703         if(this.icon){
19704             text.unshift({
19705                 tag : 'i',
19706                 cls : 'fa ' + this.icon
19707             })
19708         }
19709         
19710         
19711         var cfg = {
19712             tag : 'div',
19713             cls : 'btn-group',
19714             cn : [
19715                 {
19716                     tag : 'button',
19717                     cls : 'dropdown-button btn btn-' + this.weight,
19718                     cn : text
19719                 },
19720                 {
19721                     tag : 'button',
19722                     cls : 'dropdown-toggle btn btn-' + this.weight,
19723                     cn : [
19724                         {
19725                             tag : 'span',
19726                             cls : 'caret'
19727                         }
19728                     ]
19729                 },
19730                 {
19731                     tag : 'ul',
19732                     cls : 'dropdown-menu'
19733                 }
19734             ]
19735             
19736         };
19737         
19738         if(this.pos == 'top'){
19739             cfg.cls += ' dropup';
19740         }
19741         
19742         if(this.isSubMenu){
19743             cfg = {
19744                 tag : 'ul',
19745                 cls : 'dropdown-menu'
19746             }
19747         }
19748         
19749         return cfg;
19750     },
19751     
19752     onRender : function(ct, position)
19753     {
19754         this.isSubMenu = ct.hasClass('dropdown-submenu');
19755         
19756         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
19757     },
19758     
19759     initEvents : function() 
19760     {
19761         if(this.isSubMenu){
19762             return;
19763         }
19764         
19765         this.hidden = true;
19766         
19767         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
19768         this.triggerEl.on('click', this.onTriggerPress, this);
19769         
19770         this.buttonEl = this.el.select('button.dropdown-button', true).first();
19771         this.buttonEl.on('click', this.onClick, this);
19772         
19773     },
19774     
19775     list : function()
19776     {
19777         if(this.isSubMenu){
19778             return this.el;
19779         }
19780         
19781         return this.el.select('ul.dropdown-menu', true).first();
19782     },
19783     
19784     onClick : function(e)
19785     {
19786         this.fireEvent("click", this, e);
19787     },
19788     
19789     onTriggerPress  : function(e)
19790     {   
19791         if (this.isVisible()) {
19792             this.hide();
19793         } else {
19794             this.show();
19795         }
19796     },
19797     
19798     isVisible : function(){
19799         return !this.hidden;
19800     },
19801     
19802     show : function()
19803     {
19804         this.fireEvent("beforeshow", this);
19805         
19806         this.hidden = false;
19807         this.el.addClass('open');
19808         
19809         Roo.get(document).on("mouseup", this.onMouseUp, this);
19810         
19811         this.fireEvent("show", this);
19812         
19813         
19814     },
19815     
19816     hide : function()
19817     {
19818         this.fireEvent("beforehide", this);
19819         
19820         this.hidden = true;
19821         this.el.removeClass('open');
19822         
19823         Roo.get(document).un("mouseup", this.onMouseUp);
19824         
19825         this.fireEvent("hide", this);
19826     },
19827     
19828     onMouseUp : function()
19829     {
19830         this.hide();
19831     }
19832     
19833 });
19834
19835  
19836  /*
19837  * - LGPL
19838  *
19839  * menu item
19840  * 
19841  */
19842 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
19843
19844 /**
19845  * @class Roo.bootstrap.menu.Item
19846  * @extends Roo.bootstrap.Component
19847  * Bootstrap MenuItem class
19848  * @cfg {Boolean} submenu (true | false) default false
19849  * @cfg {String} html text of the item
19850  * @cfg {String} href the link
19851  * @cfg {Boolean} disable (true | false) default false
19852  * @cfg {Boolean} preventDefault (true | false) default true
19853  * @cfg {String} icon Font awesome icon
19854  * @cfg {String} pos Submenu align to (left | right) default right 
19855  * 
19856  * 
19857  * @constructor
19858  * Create a new Item
19859  * @param {Object} config The config object
19860  */
19861
19862
19863 Roo.bootstrap.menu.Item = function(config){
19864     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
19865     this.addEvents({
19866         /**
19867          * @event mouseover
19868          * Fires when the mouse is hovering over this menu
19869          * @param {Roo.bootstrap.menu.Item} this
19870          * @param {Roo.EventObject} e
19871          */
19872         mouseover : true,
19873         /**
19874          * @event mouseout
19875          * Fires when the mouse exits this menu
19876          * @param {Roo.bootstrap.menu.Item} this
19877          * @param {Roo.EventObject} e
19878          */
19879         mouseout : true,
19880         // raw events
19881         /**
19882          * @event click
19883          * The raw click event for the entire grid.
19884          * @param {Roo.EventObject} e
19885          */
19886         click : true
19887     });
19888 };
19889
19890 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
19891     
19892     submenu : false,
19893     href : '',
19894     html : '',
19895     preventDefault: true,
19896     disable : false,
19897     icon : false,
19898     pos : 'right',
19899     
19900     getAutoCreate : function()
19901     {
19902         var text = [
19903             {
19904                 tag : 'span',
19905                 cls : 'roo-menu-item-text',
19906                 html : this.html
19907             }
19908         ];
19909         
19910         if(this.icon){
19911             text.unshift({
19912                 tag : 'i',
19913                 cls : 'fa ' + this.icon
19914             })
19915         }
19916         
19917         var cfg = {
19918             tag : 'li',
19919             cn : [
19920                 {
19921                     tag : 'a',
19922                     href : this.href || '#',
19923                     cn : text
19924                 }
19925             ]
19926         };
19927         
19928         if(this.disable){
19929             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
19930         }
19931         
19932         if(this.submenu){
19933             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
19934             
19935             if(this.pos == 'left'){
19936                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
19937             }
19938         }
19939         
19940         return cfg;
19941     },
19942     
19943     initEvents : function() 
19944     {
19945         this.el.on('mouseover', this.onMouseOver, this);
19946         this.el.on('mouseout', this.onMouseOut, this);
19947         
19948         this.el.select('a', true).first().on('click', this.onClick, this);
19949         
19950     },
19951     
19952     onClick : function(e)
19953     {
19954         if(this.preventDefault){
19955             e.preventDefault();
19956         }
19957         
19958         this.fireEvent("click", this, e);
19959     },
19960     
19961     onMouseOver : function(e)
19962     {
19963         if(this.submenu && this.pos == 'left'){
19964             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
19965         }
19966         
19967         this.fireEvent("mouseover", this, e);
19968     },
19969     
19970     onMouseOut : function(e)
19971     {
19972         this.fireEvent("mouseout", this, e);
19973     }
19974 });
19975
19976  
19977
19978  /*
19979  * - LGPL
19980  *
19981  * menu separator
19982  * 
19983  */
19984 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
19985
19986 /**
19987  * @class Roo.bootstrap.menu.Separator
19988  * @extends Roo.bootstrap.Component
19989  * Bootstrap Separator class
19990  * 
19991  * @constructor
19992  * Create a new Separator
19993  * @param {Object} config The config object
19994  */
19995
19996
19997 Roo.bootstrap.menu.Separator = function(config){
19998     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
19999 };
20000
20001 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
20002     
20003     getAutoCreate : function(){
20004         var cfg = {
20005             tag : 'li',
20006             cls: 'divider'
20007         };
20008         
20009         return cfg;
20010     }
20011    
20012 });
20013
20014  
20015
20016