Roo.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  * @cfg {string} tooltip  Text for the tooltip
20  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
21  * 
22  * @constructor
23  * Do not use directly - it does not do anything..
24  * @param {Object} config The config object
25  */
26
27
28
29 Roo.bootstrap.Component = function(config){
30     Roo.bootstrap.Component.superclass.constructor.call(this, config);
31 };
32
33 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
34     
35     
36     allowDomMove : false, // to stop relocations in parent onRender...
37     
38     cls : false,
39     
40     style : false,
41     
42     autoCreate : false,
43     
44     tooltip : null,
45     /**
46      * Initialize Events for the element
47      */
48     initEvents : function() { },
49     
50     xattr : false,
51     
52     parentId : false,
53     
54     can_build_overlaid : true,
55     
56     container_method : false,
57     
58     dataId : false,
59     
60     name : false,
61     
62     parent: function() {
63         // returns the parent component..
64         return Roo.ComponentMgr.get(this.parentId)
65         
66         
67     },
68     
69     // private
70     onRender : function(ct, position)
71     {
72        // Roo.log("Call onRender: " + this.xtype);
73         
74         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
75         
76         if(this.el){
77             if (this.el.attr('xtype')) {
78                 this.el.attr('xtypex', this.el.attr('xtype'));
79                 this.el.dom.removeAttribute('xtype');
80                 
81                 this.initEvents();
82             }
83             
84             return;
85         }
86         
87          
88         
89         var cfg = Roo.apply({},  this.getAutoCreate());
90         cfg.id = Roo.id();
91         
92         // fill in the extra attributes 
93         if (this.xattr && typeof(this.xattr) =='object') {
94             for (var i in this.xattr) {
95                 cfg[i] = this.xattr[i];
96             }
97         }
98         
99         if(this.dataId){
100             cfg.dataId = this.dataId;
101         }
102         
103         if (this.cls) {
104             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
105         }
106         
107         if (this.style) { // fixme needs to support more complex style data.
108             cfg.style = this.style;
109         }
110         
111         if(this.name){
112             cfg.name = this.name;
113         }
114         
115        
116         
117         this.el = ct.createChild(cfg, position);
118         
119         if (this.tooltip) {
120             this.tooltipEl().attr('tooltip', this.tooltip);
121         }
122         
123         if(this.tabIndex !== undefined){
124             this.el.dom.setAttribute('tabIndex', this.tabIndex);
125         }
126         this.initEvents();
127         
128         
129     },
130     /**
131      * Fetch the element to add children to
132      * @return {Roo.Element} defaults to this.el
133      */
134     getChildContainer : function()
135     {
136         return this.el;
137     },
138     /**
139      * Fetch the element to display the tooltip on.
140      * @return {Roo.Element} defaults to this.el
141      */
142     tooltipEl : function()
143     {
144         return this.el;
145     },
146         
147     addxtype  : function(tree,cntr)
148     {
149         var cn = this;
150         
151         cn = Roo.factory(tree);
152            
153         cn.parentType = this.xtype; //??
154         cn.parentId = this.id;
155         
156         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
157         if (typeof(cn.container_method) == 'string') {
158             cntr = cn.container_method;
159         }
160         
161         
162         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
163         
164         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
165         
166         var build_from_html =  Roo.XComponent.build_from_html;
167           
168         var is_body  = (tree.xtype == 'Body') ;
169           
170         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
171           
172         var self_cntr_el = Roo.get(this[cntr](false));
173         
174         // do not try and build conditional elements 
175         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
176             return false;
177         }
178         
179         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
180             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
181                 return this.addxtypeChild(tree,cntr);
182             }
183             
184             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
185                 
186             if(echild){
187                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
188             }
189             
190             Roo.log('skipping render');
191             return cn;
192             
193         }
194         
195         var ret = false;
196         
197         while (true) {
198             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
199             
200             if (!echild) {
201                 break;
202             }
203             
204             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
205                 break;
206             }
207             
208             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
209         }
210         return ret;
211     },
212     
213     addxtypeChild : function (tree, cntr)
214     {
215         Roo.debug && Roo.log('addxtypeChild:' + cntr);
216         var cn = this;
217         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
218         
219         
220         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
221                     (typeof(tree['flexy:foreach']) != 'undefined');
222           
223         
224         
225          skip_children = false;
226         // render the element if it's not BODY.
227         if (tree.xtype != 'Body') {
228            
229             cn = Roo.factory(tree);
230            
231             cn.parentType = this.xtype; //??
232             cn.parentId = this.id;
233             
234             var build_from_html =  Roo.XComponent.build_from_html;
235             
236             
237             // does the container contain child eleemnts with 'xtype' attributes.
238             // that match this xtype..
239             // note - when we render we create these as well..
240             // so we should check to see if body has xtype set.
241             if (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
242                
243                 var self_cntr_el = Roo.get(this[cntr](false));
244                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
245                 
246                 
247                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
248                 // and are not displayed -this causes this to use up the wrong element when matching.
249                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
250                 
251                 
252                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
253                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
254                   
255                   
256                   
257                     cn.el = echild;
258                   //  Roo.log("GOT");
259                     //echild.dom.removeAttribute('xtype');
260                 } else {
261                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
262                     Roo.debug && Roo.log(self_cntr_el);
263                     Roo.debug && Roo.log(echild);
264                     Roo.debug && Roo.log(cn);
265                 }
266             }
267            
268             
269            
270             // if object has flexy:if - then it may or may not be rendered.
271             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
272                 // skip a flexy if element.
273                 Roo.debug && Roo.log('skipping render');
274                 Roo.debug && Roo.log(tree);
275                 if (!cn.el) {
276                     Roo.debug && Roo.log('skipping all children');
277                     skip_children = true;
278                 }
279                 
280              } else {
281                  
282                 // actually if flexy:foreach is found, we really want to create 
283                 // multiple copies here...
284                 //Roo.log('render');
285                 //Roo.log(this[cntr]());
286                 cn.render(this[cntr](true));
287              }
288             // then add the element..
289         }
290         
291         
292         // handle the kids..
293         
294         var nitems = [];
295         /*
296         if (typeof (tree.menu) != 'undefined') {
297             tree.menu.parentType = cn.xtype;
298             tree.menu.triggerEl = cn.el;
299             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
300             
301         }
302         */
303         if (!tree.items || !tree.items.length) {
304             cn.items = nitems;
305             return cn;
306         }
307         var items = tree.items;
308         delete tree.items;
309         
310         //Roo.log(items.length);
311             // add the items..
312         if (!skip_children) {    
313             for(var i =0;i < items.length;i++) {
314                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
315             }
316         }
317         
318         cn.items = nitems;
319         
320         return cn;
321     }
322     
323     
324     
325     
326 });
327
328  /*
329  * - LGPL
330  *
331  * Body
332  * 
333  */
334
335 /**
336  * @class Roo.bootstrap.Body
337  * @extends Roo.bootstrap.Component
338  * Bootstrap Body class
339  * 
340  * @constructor
341  * Create a new body
342  * @param {Object} config The config object
343  */
344
345 Roo.bootstrap.Body = function(config){
346     Roo.bootstrap.Body.superclass.constructor.call(this, config);
347     this.el = Roo.get(document.body);
348     if (this.cls && this.cls.length) {
349         Roo.get(document.body).addClass(this.cls);
350     }
351 };
352
353 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
354       
355         autoCreate : {
356         cls: 'container'
357     },
358     onRender : function(ct, position)
359     {
360        /* Roo.log("Roo.bootstrap.Body - onRender");
361         if (this.cls && this.cls.length) {
362             Roo.get(document.body).addClass(this.cls);
363         }
364         // style??? xttr???
365         */
366     }
367     
368     
369  
370    
371 });
372
373  /*
374  * - LGPL
375  *
376  * button group
377  * 
378  */
379
380
381 /**
382  * @class Roo.bootstrap.ButtonGroup
383  * @extends Roo.bootstrap.Component
384  * Bootstrap ButtonGroup class
385  * @cfg {String} size lg | sm | xs (default empty normal)
386  * @cfg {String} align vertical | justified  (default none)
387  * @cfg {String} direction up | down (default down)
388  * @cfg {Boolean} toolbar false | true
389  * @cfg {Boolean} btn true | false
390  * 
391  * 
392  * @constructor
393  * Create a new Input
394  * @param {Object} config The config object
395  */
396
397 Roo.bootstrap.ButtonGroup = function(config){
398     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
399 };
400
401 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
402     
403     size: '',
404     align: '',
405     direction: '',
406     toolbar: false,
407     btn: true,
408
409     getAutoCreate : function(){
410         var cfg = {
411             cls: 'btn-group',
412             html : null
413         }
414         
415         cfg.html = this.html || cfg.html;
416         
417         if (this.toolbar) {
418             cfg = {
419                 cls: 'btn-toolbar',
420                 html: null
421             }
422             
423             return cfg;
424         }
425         
426         if (['vertical','justified'].indexOf(this.align)!==-1) {
427             cfg.cls = 'btn-group-' + this.align;
428             
429             if (this.align == 'justified') {
430                 console.log(this.items);
431             }
432         }
433         
434         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
435             cfg.cls += ' btn-group-' + this.size;
436         }
437         
438         if (this.direction == 'up') {
439             cfg.cls += ' dropup' ;
440         }
441         
442         return cfg;
443     }
444    
445 });
446
447  /*
448  * - LGPL
449  *
450  * button
451  * 
452  */
453
454 /**
455  * @class Roo.bootstrap.Button
456  * @extends Roo.bootstrap.Component
457  * Bootstrap Button class
458  * @cfg {String} html The button content
459  * @cfg {String} weight (  primary | success | info | warning | danger | link ) default 
460  * @cfg {String} size ( lg | sm | xs)
461  * @cfg {String} tag ( a | input | submit)
462  * @cfg {String} href empty or href
463  * @cfg {Boolean} disabled default false;
464  * @cfg {Boolean} isClose default false;
465  * @cfg {String} glyphicon (| 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)
466  * @cfg {String} badge text for badge
467  * @cfg {String} theme default 
468  * @cfg {Boolean} inverse 
469  * @cfg {Boolean} toggle 
470  * @cfg {String} ontext text for on toggle state
471  * @cfg {String} offtext text for off toggle state
472  * @cfg {Boolean} defaulton 
473  * @cfg {Boolean} preventDefault  default true
474  * @cfg {Boolean} removeClass remove the standard class..
475  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
476  * 
477  * @constructor
478  * Create a new button
479  * @param {Object} config The config object
480  */
481
482
483 Roo.bootstrap.Button = function(config){
484     Roo.bootstrap.Button.superclass.constructor.call(this, config);
485     this.addEvents({
486         // raw events
487         /**
488          * @event click
489          * When a butotn is pressed
490          * @param {Roo.EventObject} e
491          */
492         "click" : true,
493          /**
494          * @event toggle
495          * After the button has been toggles
496          * @param {Roo.EventObject} e
497          * @param {boolean} pressed (also available as button.pressed)
498          */
499         "toggle" : true
500     });
501 };
502
503 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
504     html: false,
505     active: false,
506     weight: '',
507     size: '',
508     tag: 'button',
509     href: '',
510     disabled: false,
511     isClose: false,
512     glyphicon: '',
513     badge: '',
514     theme: 'default',
515     inverse: false,
516     
517     toggle: false,
518     ontext: 'ON',
519     offtext: 'OFF',
520     defaulton: true,
521     preventDefault: true,
522     removeClass: false,
523     name: false,
524     target: false,
525     
526     
527     pressed : null,
528      
529     
530     getAutoCreate : function(){
531         
532         var cfg = {
533             tag : 'button',
534             cls : 'roo-button',
535             html: ''
536         };
537         
538         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
539             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
540             this.tag = 'button';
541         } else {
542             cfg.tag = this.tag;
543         }
544         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
545         
546         if (this.toggle == true) {
547             cfg={
548                 tag: 'div',
549                 cls: 'slider-frame roo-button',
550                 cn: [
551                     {
552                         tag: 'span',
553                         'data-on-text':'ON',
554                         'data-off-text':'OFF',
555                         cls: 'slider-button',
556                         html: this.offtext
557                     }
558                 ]
559             };
560             
561             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
562                 cfg.cls += ' '+this.weight;
563             }
564             
565             return cfg;
566         }
567         
568         if (this.isClose) {
569             cfg.cls += ' close';
570             
571             cfg["aria-hidden"] = true;
572             
573             cfg.html = "&times;";
574             
575             return cfg;
576         }
577         
578          
579         if (this.theme==='default') {
580             cfg.cls = 'btn roo-button';
581             
582             //if (this.parentType != 'Navbar') {
583             this.weight = this.weight.length ?  this.weight : 'default';
584             //}
585             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
586                 
587                 cfg.cls += ' btn-' + this.weight;
588             }
589         } else if (this.theme==='glow') {
590             
591             cfg.tag = 'a';
592             cfg.cls = 'btn-glow roo-button';
593             
594             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
595                 
596                 cfg.cls += ' ' + this.weight;
597             }
598         }
599    
600         
601         if (this.inverse) {
602             this.cls += ' inverse';
603         }
604         
605         
606         if (this.active) {
607             cfg.cls += ' active';
608         }
609         
610         if (this.disabled) {
611             cfg.disabled = 'disabled';
612         }
613         
614         if (this.items) {
615             Roo.log('changing to ul' );
616             cfg.tag = 'ul';
617             this.glyphicon = 'caret';
618         }
619         
620         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
621          
622         //gsRoo.log(this.parentType);
623         if (this.parentType === 'Navbar' && !this.parent().bar) {
624             Roo.log('changing to li?');
625             
626             cfg.tag = 'li';
627             
628             cfg.cls = '';
629             cfg.cn =  [{
630                 tag : 'a',
631                 cls : 'roo-button',
632                 html : this.html,
633                 href : this.href || '#'
634             }];
635             if (this.menu) {
636                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
637                 cfg.cls += ' dropdown';
638             }   
639             
640             delete cfg.html;
641             
642         }
643         
644        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
645         
646         if (this.glyphicon) {
647             cfg.html = ' ' + cfg.html;
648             
649             cfg.cn = [
650                 {
651                     tag: 'span',
652                     cls: 'glyphicon glyphicon-' + this.glyphicon
653                 }
654             ];
655         }
656         
657         if (this.badge) {
658             cfg.html += ' ';
659             
660             cfg.tag = 'a';
661             
662 //            cfg.cls='btn roo-button';
663             
664             cfg.href=this.href;
665             
666             var value = cfg.html;
667             
668             if(this.glyphicon){
669                 value = {
670                             tag: 'span',
671                             cls: 'glyphicon glyphicon-' + this.glyphicon,
672                             html: this.html
673                         };
674                 
675             }
676             
677             cfg.cn = [
678                 value,
679                 {
680                     tag: 'span',
681                     cls: 'badge',
682                     html: this.badge
683                 }
684             ];
685             
686             cfg.html='';
687         }
688         
689         if (this.menu) {
690             cfg.cls += ' dropdown';
691             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
692         }
693         
694         if (cfg.tag !== 'a' && this.href !== '') {
695             throw "Tag must be a to set href.";
696         } else if (this.href.length > 0) {
697             cfg.href = this.href;
698         }
699         
700         if(this.removeClass){
701             cfg.cls = '';
702         }
703         
704         if(this.target){
705             cfg.target = this.target;
706         }
707         
708         return cfg;
709     },
710     initEvents: function() {
711        // Roo.log('init events?');
712 //        Roo.log(this.el.dom);
713         // add the menu...
714         
715         if (typeof (this.menu) != 'undefined') {
716             this.menu.parentType = this.xtype;
717             this.menu.triggerEl = this.el;
718             this.addxtype(Roo.apply({}, this.menu));
719         }
720
721
722        if (this.el.hasClass('roo-button')) {
723             this.el.on('click', this.onClick, this);
724        } else {
725             this.el.select('.roo-button').on('click', this.onClick, this);
726        }
727        
728        if(this.removeClass){
729            this.el.on('click', this.onClick, this);
730        }
731        
732        this.el.enableDisplayMode();
733         
734     },
735     onClick : function(e)
736     {
737         if (this.disabled) {
738             return;
739         }
740         
741         
742         Roo.log('button on click ');
743         if(this.preventDefault){
744             e.preventDefault();
745         }
746         if (this.pressed === true || this.pressed === false) {
747             this.pressed = !this.pressed;
748             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
749             this.fireEvent('toggle', this, e, this.pressed);
750         }
751         
752         
753         this.fireEvent('click', this, e);
754     },
755     
756     /**
757      * Enables this button
758      */
759     enable : function()
760     {
761         this.disabled = false;
762         this.el.removeClass('disabled');
763     },
764     
765     /**
766      * Disable this button
767      */
768     disable : function()
769     {
770         this.disabled = true;
771         this.el.addClass('disabled');
772     },
773      /**
774      * sets the active state on/off, 
775      * @param {Boolean} state (optional) Force a particular state
776      */
777     setActive : function(v) {
778         
779         this.el[v ? 'addClass' : 'removeClass']('active');
780     },
781      /**
782      * toggles the current active state 
783      */
784     toggleActive : function()
785     {
786        var active = this.el.hasClass('active');
787        this.setActive(!active);
788        
789         
790     },
791     setText : function(str)
792     {
793         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
794     },
795     getText : function()
796     {
797         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
798     },
799     hide: function() {
800        
801      
802         this.el.hide();   
803     },
804     show: function() {
805        
806         this.el.show();   
807     }
808     
809     
810 });
811
812  /*
813  * - LGPL
814  *
815  * column
816  * 
817  */
818
819 /**
820  * @class Roo.bootstrap.Column
821  * @extends Roo.bootstrap.Component
822  * Bootstrap Column class
823  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
824  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
825  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
826  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
827  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
828  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
829  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
830  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
831  *
832  * 
833  * @cfg {Boolean} hidden (true|false) hide the element
834  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
835  * @cfg {String} fa (ban|check|...) font awesome icon
836  * @cfg {Number} fasize (1|2|....) font awsome size
837
838  * @cfg {String} icon (info-sign|check|...) glyphicon name
839
840  * @cfg {String} html content of column.
841  * 
842  * @constructor
843  * Create a new Column
844  * @param {Object} config The config object
845  */
846
847 Roo.bootstrap.Column = function(config){
848     Roo.bootstrap.Column.superclass.constructor.call(this, config);
849 };
850
851 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
852     
853     xs: false,
854     sm: false,
855     md: false,
856     lg: false,
857     xsoff: false,
858     smoff: false,
859     mdoff: false,
860     lgoff: false,
861     html: '',
862     offset: 0,
863     alert: false,
864     fa: false,
865     icon : false,
866     hidden : false,
867     fasize : 1,
868     
869     getAutoCreate : function(){
870         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
871         
872         cfg = {
873             tag: 'div',
874             cls: 'column'
875         };
876         
877         var settings=this;
878         ['xs','sm','md','lg'].map(function(size){
879             //Roo.log( size + ':' + settings[size]);
880             
881             if (settings[size+'off'] !== false) {
882                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
883             }
884             
885             if (settings[size] === false) {
886                 return;
887             }
888             Roo.log(settings[size]);
889             if (!settings[size]) { // 0 = hidden
890                 cfg.cls += ' hidden-' + size;
891                 return;
892             }
893             cfg.cls += ' col-' + size + '-' + settings[size];
894             
895         });
896         
897         if (this.hidden) {
898             cfg.cls += ' hidden';
899         }
900         
901         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
902             cfg.cls +=' alert alert-' + this.alert;
903         }
904         
905         
906         if (this.html.length) {
907             cfg.html = this.html;
908         }
909         if (this.fa) {
910             var fasize = '';
911             if (this.fasize > 1) {
912                 fasize = ' fa-' + this.fasize + 'x';
913             }
914             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
915             
916             
917         }
918         if (this.icon) {
919             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + + (cfg.html || '')
920         }
921         
922         return cfg;
923     }
924    
925 });
926
927  
928
929  /*
930  * - LGPL
931  *
932  * page container.
933  * 
934  */
935
936
937 /**
938  * @class Roo.bootstrap.Container
939  * @extends Roo.bootstrap.Component
940  * Bootstrap Container class
941  * @cfg {Boolean} jumbotron is it a jumbotron element
942  * @cfg {String} html content of element
943  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
944  * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
945  * @cfg {String} header content of header (for panel)
946  * @cfg {String} footer content of footer (for panel)
947  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
948  * @cfg {String} tag (header|aside|section) type of HTML tag.
949  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
950  * @cfg {String} fa (ban|check|...) font awesome icon
951  * @cfg {String} icon (info-sign|check|...) glyphicon name
952  * @cfg {Boolean} hidden (true|false) hide the element
953
954  *     
955  * @constructor
956  * Create a new Container
957  * @param {Object} config The config object
958  */
959
960 Roo.bootstrap.Container = function(config){
961     Roo.bootstrap.Container.superclass.constructor.call(this, config);
962 };
963
964 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
965     
966     jumbotron : false,
967     well: '',
968     panel : '',
969     header: '',
970     footer : '',
971     sticky: '',
972     tag : false,
973     alert : false,
974     fa: false,
975     icon : false,
976   
977      
978     getChildContainer : function() {
979         
980         if(!this.el){
981             return false;
982         }
983         
984         if (this.panel.length) {
985             return this.el.select('.panel-body',true).first();
986         }
987         
988         return this.el;
989     },
990     
991     
992     getAutoCreate : function(){
993         
994         var cfg = {
995             tag : this.tag || 'div',
996             html : '',
997             cls : ''
998         };
999         if (this.jumbotron) {
1000             cfg.cls = 'jumbotron';
1001         }
1002         
1003         
1004         
1005         // - this is applied by the parent..
1006         //if (this.cls) {
1007         //    cfg.cls = this.cls + '';
1008         //}
1009         
1010         if (this.sticky.length) {
1011             
1012             var bd = Roo.get(document.body);
1013             if (!bd.hasClass('bootstrap-sticky')) {
1014                 bd.addClass('bootstrap-sticky');
1015                 Roo.select('html',true).setStyle('height', '100%');
1016             }
1017              
1018             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1019         }
1020         
1021         
1022         if (this.well.length) {
1023             switch (this.well) {
1024                 case 'lg':
1025                 case 'sm':
1026                     cfg.cls +=' well well-' +this.well;
1027                     break;
1028                 default:
1029                     cfg.cls +=' well';
1030                     break;
1031             }
1032         }
1033         
1034         if (this.hidden) {
1035             cfg.cls += ' hidden';
1036         }
1037         
1038         
1039         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1040             cfg.cls +=' alert alert-' + this.alert;
1041         }
1042         
1043         var body = cfg;
1044         
1045         if (this.panel.length) {
1046             cfg.cls += ' panel panel-' + this.panel;
1047             cfg.cn = [];
1048             if (this.header.length) {
1049                 cfg.cn.push({
1050                     
1051                     cls : 'panel-heading',
1052                     cn : [{
1053                         tag: 'h3',
1054                         cls : 'panel-title',
1055                         html : this.header
1056                     }]
1057                     
1058                 });
1059             }
1060             body = false;
1061             cfg.cn.push({
1062                 cls : 'panel-body',
1063                 html : this.html
1064             });
1065             
1066             
1067             if (this.footer.length) {
1068                 cfg.cn.push({
1069                     cls : 'panel-footer',
1070                     html : this.footer
1071                     
1072                 });
1073             }
1074             
1075         }
1076         
1077         if (body) {
1078             body.html = this.html || cfg.html;
1079             // prefix with the icons..
1080             if (this.fa) {
1081                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1082             }
1083             if (this.icon) {
1084                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1085             }
1086             
1087             
1088         }
1089         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1090             cfg.cls =  'container';
1091         }
1092         
1093         return cfg;
1094     },
1095     
1096     titleEl : function()
1097     {
1098         if(!this.el || !this.panel.length || !this.header.length){
1099             return;
1100         }
1101         
1102         return this.el.select('.panel-title',true).first();
1103     },
1104     
1105     setTitle : function(v)
1106     {
1107         var titleEl = this.titleEl();
1108         
1109         if(!titleEl){
1110             return;
1111         }
1112         
1113         titleEl.dom.innerHTML = v;
1114     },
1115     
1116     getTitle : function()
1117     {
1118         
1119         var titleEl = this.titleEl();
1120         
1121         if(!titleEl){
1122             return '';
1123         }
1124         
1125         return titleEl.dom.innerHTML;
1126     }
1127    
1128 });
1129
1130  /*
1131  * - LGPL
1132  *
1133  * image
1134  * 
1135  */
1136
1137
1138 /**
1139  * @class Roo.bootstrap.Img
1140  * @extends Roo.bootstrap.Component
1141  * Bootstrap Img class
1142  * @cfg {Boolean} imgResponsive false | true
1143  * @cfg {String} border rounded | circle | thumbnail
1144  * @cfg {String} src image source
1145  * @cfg {String} alt image alternative text
1146  * @cfg {String} href a tag href
1147  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1148  * 
1149  * @constructor
1150  * Create a new Input
1151  * @param {Object} config The config object
1152  */
1153
1154 Roo.bootstrap.Img = function(config){
1155     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1156     
1157     this.addEvents({
1158         // img events
1159         /**
1160          * @event click
1161          * The img click event for the img.
1162          * @param {Roo.EventObject} e
1163          */
1164         "click" : true
1165     });
1166 };
1167
1168 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1169     
1170     imgResponsive: true,
1171     border: '',
1172     src: '',
1173     href: false,
1174     target: false,
1175
1176     getAutoCreate : function(){
1177         
1178         var cfg = {
1179             tag: 'img',
1180             cls: (this.imgResponsive) ? 'img-responsive' : '',
1181             html : null
1182         }
1183         
1184         cfg.html = this.html || cfg.html;
1185         
1186         cfg.src = this.src || cfg.src;
1187         
1188         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1189             cfg.cls += ' img-' + this.border;
1190         }
1191         
1192         if(this.alt){
1193             cfg.alt = this.alt;
1194         }
1195         
1196         if(this.href){
1197             var a = {
1198                 tag: 'a',
1199                 href: this.href,
1200                 cn: [
1201                     cfg
1202                 ]
1203             }
1204             
1205             if(this.target){
1206                 a.target = this.target;
1207             }
1208             
1209         }
1210         
1211         
1212         return (this.href) ? a : cfg;
1213     },
1214     
1215     initEvents: function() {
1216         
1217         if(!this.href){
1218             this.el.on('click', this.onClick, this);
1219         }
1220     },
1221     
1222     onClick : function(e)
1223     {
1224         Roo.log('img onclick');
1225         this.fireEvent('click', this, e);
1226     }
1227    
1228 });
1229
1230  /*
1231  * - LGPL
1232  *
1233  * image
1234  * 
1235  */
1236
1237
1238 /**
1239  * @class Roo.bootstrap.Link
1240  * @extends Roo.bootstrap.Component
1241  * Bootstrap Link Class
1242  * @cfg {String} alt image alternative text
1243  * @cfg {String} href a tag href
1244  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1245  * @cfg {String} html the content of the link.
1246  * @cfg {String} anchor name for the anchor link
1247
1248  * @cfg {Boolean} preventDefault (true | false) default false
1249
1250  * 
1251  * @constructor
1252  * Create a new Input
1253  * @param {Object} config The config object
1254  */
1255
1256 Roo.bootstrap.Link = function(config){
1257     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1258     
1259     this.addEvents({
1260         // img events
1261         /**
1262          * @event click
1263          * The img click event for the img.
1264          * @param {Roo.EventObject} e
1265          */
1266         "click" : true
1267     });
1268 };
1269
1270 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1271     
1272     href: false,
1273     target: false,
1274     preventDefault: false,
1275     anchor : false,
1276     alt : false,
1277
1278     getAutoCreate : function()
1279     {
1280         
1281         var cfg = {
1282             tag: 'a'
1283         };
1284         // anchor's do not require html/href...
1285         if (this.anchor === false) {
1286             cfg.html = this.html || 'html-missing';
1287             cfg.href = this.href || '#';
1288         } else {
1289             cfg.name = this.anchor;
1290             if (this.html !== false) {
1291                 cfg.html = this.html;
1292             }
1293             if (this.href !== false) {
1294                 cfg.href = this.href;
1295             }
1296         }
1297         
1298         if(this.alt !== false){
1299             cfg.alt = this.alt;
1300         }
1301         
1302         
1303         if(this.target !== false) {
1304             cfg.target = this.target;
1305         }
1306         
1307         return cfg;
1308     },
1309     
1310     initEvents: function() {
1311         
1312         if(!this.href || this.preventDefault){
1313             this.el.on('click', this.onClick, this);
1314         }
1315     },
1316     
1317     onClick : function(e)
1318     {
1319         if(this.preventDefault){
1320             e.preventDefault();
1321         }
1322         //Roo.log('img onclick');
1323         this.fireEvent('click', this, e);
1324     }
1325    
1326 });
1327
1328  /*
1329  * - LGPL
1330  *
1331  * header
1332  * 
1333  */
1334
1335 /**
1336  * @class Roo.bootstrap.Header
1337  * @extends Roo.bootstrap.Component
1338  * Bootstrap Header class
1339  * @cfg {String} html content of header
1340  * @cfg {Number} level (1|2|3|4|5|6) default 1
1341  * 
1342  * @constructor
1343  * Create a new Header
1344  * @param {Object} config The config object
1345  */
1346
1347
1348 Roo.bootstrap.Header  = function(config){
1349     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1350 };
1351
1352 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1353     
1354     //href : false,
1355     html : false,
1356     level : 1,
1357     
1358     
1359     
1360     getAutoCreate : function(){
1361         
1362         var cfg = {
1363             tag: 'h' + (1 *this.level),
1364             html: this.html || 'fill in html'
1365         } ;
1366         
1367         return cfg;
1368     }
1369    
1370 });
1371
1372  
1373
1374  /*
1375  * Based on:
1376  * Ext JS Library 1.1.1
1377  * Copyright(c) 2006-2007, Ext JS, LLC.
1378  *
1379  * Originally Released Under LGPL - original licence link has changed is not relivant.
1380  *
1381  * Fork - LGPL
1382  * <script type="text/javascript">
1383  */
1384  
1385 /**
1386  * @class Roo.bootstrap.MenuMgr
1387  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1388  * @singleton
1389  */
1390 Roo.bootstrap.MenuMgr = function(){
1391    var menus, active, groups = {}, attached = false, lastShow = new Date();
1392
1393    // private - called when first menu is created
1394    function init(){
1395        menus = {};
1396        active = new Roo.util.MixedCollection();
1397        Roo.get(document).addKeyListener(27, function(){
1398            if(active.length > 0){
1399                hideAll();
1400            }
1401        });
1402    }
1403
1404    // private
1405    function hideAll(){
1406        if(active && active.length > 0){
1407            var c = active.clone();
1408            c.each(function(m){
1409                m.hide();
1410            });
1411        }
1412    }
1413
1414    // private
1415    function onHide(m){
1416        active.remove(m);
1417        if(active.length < 1){
1418            Roo.get(document).un("mouseup", onMouseDown);
1419             
1420            attached = false;
1421        }
1422    }
1423
1424    // private
1425    function onShow(m){
1426        var last = active.last();
1427        lastShow = new Date();
1428        active.add(m);
1429        if(!attached){
1430           Roo.get(document).on("mouseup", onMouseDown);
1431            
1432            attached = true;
1433        }
1434        if(m.parentMenu){
1435           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1436           m.parentMenu.activeChild = m;
1437        }else if(last && last.isVisible()){
1438           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1439        }
1440    }
1441
1442    // private
1443    function onBeforeHide(m){
1444        if(m.activeChild){
1445            m.activeChild.hide();
1446        }
1447        if(m.autoHideTimer){
1448            clearTimeout(m.autoHideTimer);
1449            delete m.autoHideTimer;
1450        }
1451    }
1452
1453    // private
1454    function onBeforeShow(m){
1455        var pm = m.parentMenu;
1456        if(!pm && !m.allowOtherMenus){
1457            hideAll();
1458        }else if(pm && pm.activeChild && active != m){
1459            pm.activeChild.hide();
1460        }
1461    }
1462
1463    // private
1464    function onMouseDown(e){
1465         Roo.log("on MouseDown");
1466         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu") && !e.getTarget('.user-menu')){
1467            hideAll();
1468         }
1469         
1470         
1471    }
1472
1473    // private
1474    function onBeforeCheck(mi, state){
1475        if(state){
1476            var g = groups[mi.group];
1477            for(var i = 0, l = g.length; i < l; i++){
1478                if(g[i] != mi){
1479                    g[i].setChecked(false);
1480                }
1481            }
1482        }
1483    }
1484
1485    return {
1486
1487        /**
1488         * Hides all menus that are currently visible
1489         */
1490        hideAll : function(){
1491             hideAll();  
1492        },
1493
1494        // private
1495        register : function(menu){
1496            if(!menus){
1497                init();
1498            }
1499            menus[menu.id] = menu;
1500            menu.on("beforehide", onBeforeHide);
1501            menu.on("hide", onHide);
1502            menu.on("beforeshow", onBeforeShow);
1503            menu.on("show", onShow);
1504            var g = menu.group;
1505            if(g && menu.events["checkchange"]){
1506                if(!groups[g]){
1507                    groups[g] = [];
1508                }
1509                groups[g].push(menu);
1510                menu.on("checkchange", onCheck);
1511            }
1512        },
1513
1514         /**
1515          * Returns a {@link Roo.menu.Menu} object
1516          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1517          * be used to generate and return a new Menu instance.
1518          */
1519        get : function(menu){
1520            if(typeof menu == "string"){ // menu id
1521                return menus[menu];
1522            }else if(menu.events){  // menu instance
1523                return menu;
1524            }
1525            /*else if(typeof menu.length == 'number'){ // array of menu items?
1526                return new Roo.bootstrap.Menu({items:menu});
1527            }else{ // otherwise, must be a config
1528                return new Roo.bootstrap.Menu(menu);
1529            }
1530            */
1531            return false;
1532        },
1533
1534        // private
1535        unregister : function(menu){
1536            delete menus[menu.id];
1537            menu.un("beforehide", onBeforeHide);
1538            menu.un("hide", onHide);
1539            menu.un("beforeshow", onBeforeShow);
1540            menu.un("show", onShow);
1541            var g = menu.group;
1542            if(g && menu.events["checkchange"]){
1543                groups[g].remove(menu);
1544                menu.un("checkchange", onCheck);
1545            }
1546        },
1547
1548        // private
1549        registerCheckable : function(menuItem){
1550            var g = menuItem.group;
1551            if(g){
1552                if(!groups[g]){
1553                    groups[g] = [];
1554                }
1555                groups[g].push(menuItem);
1556                menuItem.on("beforecheckchange", onBeforeCheck);
1557            }
1558        },
1559
1560        // private
1561        unregisterCheckable : function(menuItem){
1562            var g = menuItem.group;
1563            if(g){
1564                groups[g].remove(menuItem);
1565                menuItem.un("beforecheckchange", onBeforeCheck);
1566            }
1567        }
1568    };
1569 }();/*
1570  * - LGPL
1571  *
1572  * menu
1573  * 
1574  */
1575
1576 /**
1577  * @class Roo.bootstrap.Menu
1578  * @extends Roo.bootstrap.Component
1579  * Bootstrap Menu class - container for MenuItems
1580  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1581  * 
1582  * @constructor
1583  * Create a new Menu
1584  * @param {Object} config The config object
1585  */
1586
1587
1588 Roo.bootstrap.Menu = function(config){
1589     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1590     if (this.registerMenu) {
1591         Roo.bootstrap.MenuMgr.register(this);
1592     }
1593     this.addEvents({
1594         /**
1595          * @event beforeshow
1596          * Fires before this menu is displayed
1597          * @param {Roo.menu.Menu} this
1598          */
1599         beforeshow : true,
1600         /**
1601          * @event beforehide
1602          * Fires before this menu is hidden
1603          * @param {Roo.menu.Menu} this
1604          */
1605         beforehide : true,
1606         /**
1607          * @event show
1608          * Fires after this menu is displayed
1609          * @param {Roo.menu.Menu} this
1610          */
1611         show : true,
1612         /**
1613          * @event hide
1614          * Fires after this menu is hidden
1615          * @param {Roo.menu.Menu} this
1616          */
1617         hide : true,
1618         /**
1619          * @event click
1620          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1621          * @param {Roo.menu.Menu} this
1622          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1623          * @param {Roo.EventObject} e
1624          */
1625         click : true,
1626         /**
1627          * @event mouseover
1628          * Fires when the mouse is hovering over this menu
1629          * @param {Roo.menu.Menu} this
1630          * @param {Roo.EventObject} e
1631          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1632          */
1633         mouseover : true,
1634         /**
1635          * @event mouseout
1636          * Fires when the mouse exits this menu
1637          * @param {Roo.menu.Menu} this
1638          * @param {Roo.EventObject} e
1639          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1640          */
1641         mouseout : true,
1642         /**
1643          * @event itemclick
1644          * Fires when a menu item contained in this menu is clicked
1645          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1646          * @param {Roo.EventObject} e
1647          */
1648         itemclick: true
1649     });
1650     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1651 };
1652
1653 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1654     
1655    /// html : false,
1656     //align : '',
1657     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1658     type: false,
1659     /**
1660      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1661      */
1662     registerMenu : true,
1663     
1664     menuItems :false, // stores the menu items..
1665     
1666     hidden:true,
1667     
1668     parentMenu : false,
1669     
1670     getChildContainer : function() {
1671         return this.el;  
1672     },
1673     
1674     getAutoCreate : function(){
1675          
1676         //if (['right'].indexOf(this.align)!==-1) {
1677         //    cfg.cn[1].cls += ' pull-right'
1678         //}
1679         
1680         
1681         var cfg = {
1682             tag : 'ul',
1683             cls : 'dropdown-menu' ,
1684             style : 'z-index:1000'
1685             
1686         }
1687         
1688         if (this.type === 'submenu') {
1689             cfg.cls = 'submenu active';
1690         }
1691         if (this.type === 'treeview') {
1692             cfg.cls = 'treeview-menu';
1693         }
1694         
1695         return cfg;
1696     },
1697     initEvents : function() {
1698         
1699        // Roo.log("ADD event");
1700        // Roo.log(this.triggerEl.dom);
1701         this.triggerEl.on('click', this.onTriggerPress, this);
1702         this.triggerEl.addClass('dropdown-toggle');
1703         this.el.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
1704
1705         this.el.on("mouseover", this.onMouseOver, this);
1706         this.el.on("mouseout", this.onMouseOut, this);
1707         
1708         
1709     },
1710     findTargetItem : function(e){
1711         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
1712         if(!t){
1713             return false;
1714         }
1715         //Roo.log(t);         Roo.log(t.id);
1716         if(t && t.id){
1717             //Roo.log(this.menuitems);
1718             return this.menuitems.get(t.id);
1719             
1720             //return this.items.get(t.menuItemId);
1721         }
1722         
1723         return false;
1724     },
1725     onClick : function(e){
1726         Roo.log("menu.onClick");
1727         var t = this.findTargetItem(e);
1728         if(!t || t.isContainer){
1729             return;
1730         }
1731         Roo.log(e);
1732         /*
1733         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
1734             if(t == this.activeItem && t.shouldDeactivate(e)){
1735                 this.activeItem.deactivate();
1736                 delete this.activeItem;
1737                 return;
1738             }
1739             if(t.canActivate){
1740                 this.setActiveItem(t, true);
1741             }
1742             return;
1743             
1744             
1745         }
1746         */
1747        
1748         Roo.log('pass click event');
1749         
1750         t.onClick(e);
1751         
1752         this.fireEvent("click", this, t, e);
1753         
1754         this.hide();
1755     },
1756      onMouseOver : function(e){
1757         var t  = this.findTargetItem(e);
1758         //Roo.log(t);
1759         //if(t){
1760         //    if(t.canActivate && !t.disabled){
1761         //        this.setActiveItem(t, true);
1762         //    }
1763         //}
1764         
1765         this.fireEvent("mouseover", this, e, t);
1766     },
1767     isVisible : function(){
1768         return !this.hidden;
1769     },
1770      onMouseOut : function(e){
1771         var t  = this.findTargetItem(e);
1772         
1773         //if(t ){
1774         //    if(t == this.activeItem && t.shouldDeactivate(e)){
1775         //        this.activeItem.deactivate();
1776         //        delete this.activeItem;
1777         //    }
1778         //}
1779         this.fireEvent("mouseout", this, e, t);
1780     },
1781     
1782     
1783     /**
1784      * Displays this menu relative to another element
1785      * @param {String/HTMLElement/Roo.Element} element The element to align to
1786      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
1787      * the element (defaults to this.defaultAlign)
1788      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1789      */
1790     show : function(el, pos, parentMenu){
1791         this.parentMenu = parentMenu;
1792         if(!this.el){
1793             this.render();
1794         }
1795         this.fireEvent("beforeshow", this);
1796         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
1797     },
1798      /**
1799      * Displays this menu at a specific xy position
1800      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
1801      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1802      */
1803     showAt : function(xy, parentMenu, /* private: */_e){
1804         this.parentMenu = parentMenu;
1805         if(!this.el){
1806             this.render();
1807         }
1808         if(_e !== false){
1809             this.fireEvent("beforeshow", this);
1810             
1811             //xy = this.el.adjustForConstraints(xy);
1812         }
1813         //this.el.setXY(xy);
1814         //this.el.show();
1815         this.hideMenuItems();
1816         this.hidden = false;
1817         this.triggerEl.addClass('open');
1818         this.focus();
1819         this.fireEvent("show", this);
1820     },
1821     
1822     focus : function(){
1823         return;
1824         if(!this.hidden){
1825             this.doFocus.defer(50, this);
1826         }
1827     },
1828
1829     doFocus : function(){
1830         if(!this.hidden){
1831             this.focusEl.focus();
1832         }
1833     },
1834
1835     /**
1836      * Hides this menu and optionally all parent menus
1837      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
1838      */
1839     hide : function(deep){
1840         
1841         this.hideMenuItems();
1842         if(this.el && this.isVisible()){
1843             this.fireEvent("beforehide", this);
1844             if(this.activeItem){
1845                 this.activeItem.deactivate();
1846                 this.activeItem = null;
1847             }
1848             this.triggerEl.removeClass('open');;
1849             this.hidden = true;
1850             this.fireEvent("hide", this);
1851         }
1852         if(deep === true && this.parentMenu){
1853             this.parentMenu.hide(true);
1854         }
1855     },
1856     
1857     onTriggerPress  : function(e)
1858     {
1859         
1860         Roo.log('trigger press');
1861         //Roo.log(e.getTarget());
1862        // Roo.log(this.triggerEl.dom);
1863         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
1864             return;
1865         }
1866         if (this.isVisible()) {
1867             Roo.log('hide');
1868             this.hide();
1869         } else {
1870             this.show(this.triggerEl, false, false);
1871         }
1872         
1873         
1874     },
1875     
1876          
1877        
1878     
1879     hideMenuItems : function()
1880     {
1881         //$(backdrop).remove()
1882         Roo.select('.open',true).each(function(aa) {
1883             
1884             aa.removeClass('open');
1885           //var parent = getParent($(this))
1886           //var relatedTarget = { relatedTarget: this }
1887           
1888            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
1889           //if (e.isDefaultPrevented()) return
1890            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
1891         })
1892     },
1893     addxtypeChild : function (tree, cntr) {
1894         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
1895           
1896         this.menuitems.add(comp);
1897         return comp;
1898
1899     },
1900     getEl : function()
1901     {
1902         Roo.log(this.el);
1903         return this.el;
1904     }
1905 });
1906
1907  
1908  /*
1909  * - LGPL
1910  *
1911  * menu item
1912  * 
1913  */
1914
1915
1916 /**
1917  * @class Roo.bootstrap.MenuItem
1918  * @extends Roo.bootstrap.Component
1919  * Bootstrap MenuItem class
1920  * @cfg {String} html the menu label
1921  * @cfg {String} href the link
1922  * @cfg {Boolean} preventDefault (true | false) default true
1923  * @cfg {Boolean} isContainer (true | false) default false
1924  * 
1925  * 
1926  * @constructor
1927  * Create a new MenuItem
1928  * @param {Object} config The config object
1929  */
1930
1931
1932 Roo.bootstrap.MenuItem = function(config){
1933     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
1934     this.addEvents({
1935         // raw events
1936         /**
1937          * @event click
1938          * The raw click event for the entire grid.
1939          * @param {Roo.EventObject} e
1940          */
1941         "click" : true
1942     });
1943 };
1944
1945 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
1946     
1947     href : false,
1948     html : false,
1949     preventDefault: true,
1950     isContainer : false,
1951     
1952     getAutoCreate : function(){
1953         
1954         if(this.isContainer){
1955             return {
1956                 tag: 'li',
1957                 cls: 'dropdown-menu-item'
1958             };
1959         }
1960         
1961         var cfg= {
1962             tag: 'li',
1963             cls: 'dropdown-menu-item',
1964             cn: [
1965                     {
1966                         tag : 'a',
1967                         href : '#',
1968                         html : 'Link'
1969                     }
1970                 ]
1971         };
1972         if (this.parent().type == 'treeview') {
1973             cfg.cls = 'treeview-menu';
1974         }
1975         
1976         cfg.cn[0].href = this.href || cfg.cn[0].href ;
1977         cfg.cn[0].html = this.html || cfg.cn[0].html ;
1978         return cfg;
1979     },
1980     
1981     initEvents: function() {
1982         
1983         //this.el.select('a').on('click', this.onClick, this);
1984         
1985     },
1986     onClick : function(e)
1987     {
1988         Roo.log('item on click ');
1989         //if(this.preventDefault){
1990         //    e.preventDefault();
1991         //}
1992         //this.parent().hideMenuItems();
1993         
1994         this.fireEvent('click', this, e);
1995     },
1996     getEl : function()
1997     {
1998         return this.el;
1999     }
2000 });
2001
2002  
2003
2004  /*
2005  * - LGPL
2006  *
2007  * menu separator
2008  * 
2009  */
2010
2011
2012 /**
2013  * @class Roo.bootstrap.MenuSeparator
2014  * @extends Roo.bootstrap.Component
2015  * Bootstrap MenuSeparator class
2016  * 
2017  * @constructor
2018  * Create a new MenuItem
2019  * @param {Object} config The config object
2020  */
2021
2022
2023 Roo.bootstrap.MenuSeparator = function(config){
2024     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2025 };
2026
2027 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2028     
2029     getAutoCreate : function(){
2030         var cfg = {
2031             cls: 'divider',
2032             tag : 'li'
2033         };
2034         
2035         return cfg;
2036     }
2037    
2038 });
2039
2040  
2041
2042  
2043 /*
2044 <div class="modal fade">
2045   <div class="modal-dialog">
2046     <div class="modal-content">
2047       <div class="modal-header">
2048         <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
2049         <h4 class="modal-title">Modal title</h4>
2050       </div>
2051       <div class="modal-body">
2052         <p>One fine body&hellip;</p>
2053       </div>
2054       <div class="modal-footer">
2055         <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
2056         <button type="button" class="btn btn-primary">Save changes</button>
2057       </div>
2058     </div><!-- /.modal-content -->
2059   </div><!-- /.modal-dialog -->
2060 </div><!-- /.modal -->
2061 */
2062 /*
2063  * - LGPL
2064  *
2065  * page contgainer.
2066  * 
2067  */
2068
2069 /**
2070  * @class Roo.bootstrap.Modal
2071  * @extends Roo.bootstrap.Component
2072  * Bootstrap Modal class
2073  * @cfg {String} title Title of dialog
2074  * @cfg {Boolean} specificTitle default false
2075  * @cfg {Array} buttons Array of buttons or standard button set..
2076  * @cfg {String} buttonPosition (left|right|center) default right
2077  * @cfg {Boolean} animate default true
2078  * @cfg {Boolean} allow_close default true
2079  * 
2080  * @constructor
2081  * Create a new Modal Dialog
2082  * @param {Object} config The config object
2083  */
2084
2085 Roo.bootstrap.Modal = function(config){
2086     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2087     this.addEvents({
2088         // raw events
2089         /**
2090          * @event btnclick
2091          * The raw btnclick event for the button
2092          * @param {Roo.EventObject} e
2093          */
2094         "btnclick" : true
2095     });
2096     this.buttons = this.buttons || [];
2097 };
2098
2099 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2100     
2101     title : 'test dialog',
2102    
2103     buttons : false,
2104     
2105     // set on load...
2106     body:  false,
2107     
2108     specificTitle: false,
2109     
2110     buttonPosition: 'right',
2111     
2112     allow_close : true,
2113     
2114     animate : true,
2115     
2116     onRender : function(ct, position)
2117     {
2118         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2119      
2120         if(!this.el){
2121             var cfg = Roo.apply({},  this.getAutoCreate());
2122             cfg.id = Roo.id();
2123             //if(!cfg.name){
2124             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2125             //}
2126             //if (!cfg.name.length) {
2127             //    delete cfg.name;
2128            // }
2129             if (this.cls) {
2130                 cfg.cls += ' ' + this.cls;
2131             }
2132             if (this.style) {
2133                 cfg.style = this.style;
2134             }
2135             this.el = Roo.get(document.body).createChild(cfg, position);
2136         }
2137         //var type = this.el.dom.type;
2138         
2139         if(this.tabIndex !== undefined){
2140             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2141         }
2142         
2143         
2144         
2145         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2146         this.maskEl.enableDisplayMode("block");
2147         this.maskEl.hide();
2148         //this.el.addClass("x-dlg-modal");
2149     
2150         if (this.buttons.length) {
2151             Roo.each(this.buttons, function(bb) {
2152                 b = Roo.apply({}, bb);
2153                 b.xns = b.xns || Roo.bootstrap;
2154                 b.xtype = b.xtype || 'Button';
2155                 if (typeof(b.listeners) == 'undefined') {
2156                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2157                 }
2158                 
2159                 var btn = Roo.factory(b);
2160                 
2161                 btn.onRender(this.el.select('.modal-footer div').first());
2162                 
2163             },this);
2164         }
2165         // render the children.
2166         var nitems = [];
2167         
2168         if(typeof(this.items) != 'undefined'){
2169             var items = this.items;
2170             delete this.items;
2171
2172             for(var i =0;i < items.length;i++) {
2173                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2174             }
2175         }
2176         
2177         this.items = nitems;
2178         
2179         this.body = this.el.select('.modal-body',true).first();
2180         this.close = this.el.select('.modal-header .close', true).first();
2181         this.footer = this.el.select('.modal-footer',true).first();
2182         this.initEvents();
2183         //this.el.addClass([this.fieldClass, this.cls]);
2184         
2185     },
2186     getAutoCreate : function(){
2187         
2188         
2189         var bdy = {
2190                 cls : 'modal-body',
2191                 html : this.html || ''
2192         };
2193         
2194         var title = {
2195             tag: 'h4',
2196             cls : 'modal-title',
2197             html : this.title
2198         };
2199         
2200         if(this.specificTitle){
2201             title = this.title;
2202             
2203         };
2204         
2205         var header = [];
2206         if (this.allow_close) {
2207             header.push({
2208                 tag: 'button',
2209                 cls : 'close',
2210                 html : '&times'
2211             });
2212         }
2213         header.push(title);
2214         
2215         var modal = {
2216             cls: "modal",
2217             style : 'display: none',
2218             cn : [
2219                 {
2220                     cls: "modal-dialog",
2221                     cn : [
2222                         {
2223                             cls : "modal-content",
2224                             cn : [
2225                                 {
2226                                     cls : 'modal-header',
2227                                     cn : header
2228                                 },
2229                                 bdy,
2230                                 {
2231                                     cls : 'modal-footer',
2232                                     cn : [
2233                                         {
2234                                             tag: 'div',
2235                                             cls: 'btn-' + this.buttonPosition
2236                                         }
2237                                     ]
2238                                     
2239                                 }
2240                                 
2241                                 
2242                             ]
2243                             
2244                         }
2245                     ]
2246                         
2247                 }
2248             ]
2249         };
2250         
2251         if(this.animate){
2252             modal.cls += ' fade';
2253         }
2254         
2255         return modal;
2256           
2257     },
2258     getChildContainer : function() {
2259          
2260          return this.el.select('.modal-body',true).first();
2261         
2262     },
2263     getButtonContainer : function() {
2264          return this.el.select('.modal-footer div',true).first();
2265         
2266     },
2267     initEvents : function()
2268     {
2269         this.el.select('.modal-header .close').on('click', this.hide, this);
2270 //        
2271 //        this.addxtype(this);
2272     },
2273     show : function() {
2274         
2275         if (!this.rendered) {
2276             this.render();
2277         }
2278         
2279         this.el.setStyle('display', 'block');
2280         
2281         if(this.animate){
2282             var _this = this;
2283             (function(){ _this.el.addClass('in'); }).defer(50);
2284         }else{
2285             this.el.addClass('in');
2286         }
2287         
2288         Roo.get(document.body).addClass("x-body-masked");
2289         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2290         this.maskEl.show();
2291         this.el.setStyle('zIndex', '10001');
2292        
2293         this.fireEvent('show', this);
2294         
2295         
2296     },
2297     hide : function()
2298     {
2299         this.maskEl.hide();
2300         Roo.get(document.body).removeClass("x-body-masked");
2301         this.el.removeClass('in');
2302         
2303         if(this.animate){
2304             var _this = this;
2305             (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2306         }else{
2307             this.el.setStyle('display', 'none');
2308         }
2309         
2310         this.fireEvent('hide', this);
2311     },
2312     
2313     addButton : function(str, cb)
2314     {
2315          
2316         
2317         var b = Roo.apply({}, { html : str } );
2318         b.xns = b.xns || Roo.bootstrap;
2319         b.xtype = b.xtype || 'Button';
2320         if (typeof(b.listeners) == 'undefined') {
2321             b.listeners = { click : cb.createDelegate(this)  };
2322         }
2323         
2324         var btn = Roo.factory(b);
2325            
2326         btn.onRender(this.el.select('.modal-footer div').first());
2327         
2328         return btn;   
2329        
2330     },
2331     
2332     setDefaultButton : function(btn)
2333     {
2334         //this.el.select('.modal-footer').()
2335     },
2336     resizeTo: function(w,h)
2337     {
2338         // skip..
2339     },
2340     setContentSize  : function(w, h)
2341     {
2342         
2343     },
2344     onButtonClick: function(btn,e)
2345     {
2346         //Roo.log([a,b,c]);
2347         this.fireEvent('btnclick', btn.name, e);
2348     },
2349     setTitle: function(str) {
2350         this.el.select('.modal-title',true).first().dom.innerHTML = str;
2351         
2352     }
2353 });
2354
2355
2356 Roo.apply(Roo.bootstrap.Modal,  {
2357     /**
2358          * Button config that displays a single OK button
2359          * @type Object
2360          */
2361         OK :  [{
2362             name : 'ok',
2363             weight : 'primary',
2364             html : 'OK'
2365         }], 
2366         /**
2367          * Button config that displays Yes and No buttons
2368          * @type Object
2369          */
2370         YESNO : [
2371             {
2372                 name  : 'no',
2373                 html : 'No'
2374             },
2375             {
2376                 name  :'yes',
2377                 weight : 'primary',
2378                 html : 'Yes'
2379             }
2380         ],
2381         
2382         /**
2383          * Button config that displays OK and Cancel buttons
2384          * @type Object
2385          */
2386         OKCANCEL : [
2387             {
2388                name : 'cancel',
2389                 html : 'Cancel'
2390             },
2391             {
2392                 name : 'ok',
2393                 weight : 'primary',
2394                 html : 'OK'
2395             }
2396         ],
2397         /**
2398          * Button config that displays Yes, No and Cancel buttons
2399          * @type Object
2400          */
2401         YESNOCANCEL : [
2402             {
2403                 name : 'yes',
2404                 weight : 'primary',
2405                 html : 'Yes'
2406             },
2407             {
2408                 name : 'no',
2409                 html : 'No'
2410             },
2411             {
2412                 name : 'cancel',
2413                 html : 'Cancel'
2414             }
2415         ]
2416 });
2417  /*
2418  * - LGPL
2419  *
2420  * messagebox - can be used as a replace
2421  * 
2422  */
2423 /**
2424  * @class Roo.MessageBox
2425  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2426  * Example usage:
2427  *<pre><code>
2428 // Basic alert:
2429 Roo.Msg.alert('Status', 'Changes saved successfully.');
2430
2431 // Prompt for user data:
2432 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2433     if (btn == 'ok'){
2434         // process text value...
2435     }
2436 });
2437
2438 // Show a dialog using config options:
2439 Roo.Msg.show({
2440    title:'Save Changes?',
2441    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2442    buttons: Roo.Msg.YESNOCANCEL,
2443    fn: processResult,
2444    animEl: 'elId'
2445 });
2446 </code></pre>
2447  * @singleton
2448  */
2449 Roo.bootstrap.MessageBox = function(){
2450     var dlg, opt, mask, waitTimer;
2451     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2452     var buttons, activeTextEl, bwidth;
2453
2454     
2455     // private
2456     var handleButton = function(button){
2457         dlg.hide();
2458         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2459     };
2460
2461     // private
2462     var handleHide = function(){
2463         if(opt && opt.cls){
2464             dlg.el.removeClass(opt.cls);
2465         }
2466         //if(waitTimer){
2467         //    Roo.TaskMgr.stop(waitTimer);
2468         //    waitTimer = null;
2469         //}
2470     };
2471
2472     // private
2473     var updateButtons = function(b){
2474         var width = 0;
2475         if(!b){
2476             buttons["ok"].hide();
2477             buttons["cancel"].hide();
2478             buttons["yes"].hide();
2479             buttons["no"].hide();
2480             //dlg.footer.dom.style.display = 'none';
2481             return width;
2482         }
2483         dlg.footer.dom.style.display = '';
2484         for(var k in buttons){
2485             if(typeof buttons[k] != "function"){
2486                 if(b[k]){
2487                     buttons[k].show();
2488                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2489                     width += buttons[k].el.getWidth()+15;
2490                 }else{
2491                     buttons[k].hide();
2492                 }
2493             }
2494         }
2495         return width;
2496     };
2497
2498     // private
2499     var handleEsc = function(d, k, e){
2500         if(opt && opt.closable !== false){
2501             dlg.hide();
2502         }
2503         if(e){
2504             e.stopEvent();
2505         }
2506     };
2507
2508     return {
2509         /**
2510          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2511          * @return {Roo.BasicDialog} The BasicDialog element
2512          */
2513         getDialog : function(){
2514            if(!dlg){
2515                 dlg = new Roo.bootstrap.Modal( {
2516                     //draggable: true,
2517                     //resizable:false,
2518                     //constraintoviewport:false,
2519                     //fixedcenter:true,
2520                     //collapsible : false,
2521                     //shim:true,
2522                     //modal: true,
2523                   //  width:400,
2524                   //  height:100,
2525                     //buttonAlign:"center",
2526                     closeClick : function(){
2527                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2528                             handleButton("no");
2529                         }else{
2530                             handleButton("cancel");
2531                         }
2532                     }
2533                 });
2534                 dlg.render();
2535                 dlg.on("hide", handleHide);
2536                 mask = dlg.mask;
2537                 //dlg.addKeyListener(27, handleEsc);
2538                 buttons = {};
2539                 this.buttons = buttons;
2540                 var bt = this.buttonText;
2541                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2542                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2543                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2544                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2545                 Roo.log(buttons)
2546                 bodyEl = dlg.body.createChild({
2547
2548                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2549                         '<textarea class="roo-mb-textarea"></textarea>' +
2550                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
2551                 });
2552                 msgEl = bodyEl.dom.firstChild;
2553                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2554                 textboxEl.enableDisplayMode();
2555                 textboxEl.addKeyListener([10,13], function(){
2556                     if(dlg.isVisible() && opt && opt.buttons){
2557                         if(opt.buttons.ok){
2558                             handleButton("ok");
2559                         }else if(opt.buttons.yes){
2560                             handleButton("yes");
2561                         }
2562                     }
2563                 });
2564                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2565                 textareaEl.enableDisplayMode();
2566                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2567                 progressEl.enableDisplayMode();
2568                 var pf = progressEl.dom.firstChild;
2569                 if (pf) {
2570                     pp = Roo.get(pf.firstChild);
2571                     pp.setHeight(pf.offsetHeight);
2572                 }
2573                 
2574             }
2575             return dlg;
2576         },
2577
2578         /**
2579          * Updates the message box body text
2580          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2581          * the XHTML-compliant non-breaking space character '&amp;#160;')
2582          * @return {Roo.MessageBox} This message box
2583          */
2584         updateText : function(text){
2585             if(!dlg.isVisible() && !opt.width){
2586                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2587             }
2588             msgEl.innerHTML = text || '&#160;';
2589       
2590             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2591             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2592             var w = Math.max(
2593                     Math.min(opt.width || cw , this.maxWidth), 
2594                     Math.max(opt.minWidth || this.minWidth, bwidth)
2595             );
2596             if(opt.prompt){
2597                 activeTextEl.setWidth(w);
2598             }
2599             if(dlg.isVisible()){
2600                 dlg.fixedcenter = false;
2601             }
2602             // to big, make it scroll. = But as usual stupid IE does not support
2603             // !important..
2604             
2605             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2606                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2607                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2608             } else {
2609                 bodyEl.dom.style.height = '';
2610                 bodyEl.dom.style.overflowY = '';
2611             }
2612             if (cw > w) {
2613                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2614             } else {
2615                 bodyEl.dom.style.overflowX = '';
2616             }
2617             
2618             dlg.setContentSize(w, bodyEl.getHeight());
2619             if(dlg.isVisible()){
2620                 dlg.fixedcenter = true;
2621             }
2622             return this;
2623         },
2624
2625         /**
2626          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
2627          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2628          * @param {Number} value Any number between 0 and 1 (e.g., .5)
2629          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2630          * @return {Roo.MessageBox} This message box
2631          */
2632         updateProgress : function(value, text){
2633             if(text){
2634                 this.updateText(text);
2635             }
2636             if (pp) { // weird bug on my firefox - for some reason this is not defined
2637                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2638             }
2639             return this;
2640         },        
2641
2642         /**
2643          * Returns true if the message box is currently displayed
2644          * @return {Boolean} True if the message box is visible, else false
2645          */
2646         isVisible : function(){
2647             return dlg && dlg.isVisible();  
2648         },
2649
2650         /**
2651          * Hides the message box if it is displayed
2652          */
2653         hide : function(){
2654             if(this.isVisible()){
2655                 dlg.hide();
2656             }  
2657         },
2658
2659         /**
2660          * Displays a new message box, or reinitializes an existing message box, based on the config options
2661          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2662          * The following config object properties are supported:
2663          * <pre>
2664 Property    Type             Description
2665 ----------  ---------------  ------------------------------------------------------------------------------------
2666 animEl            String/Element   An id or Element from which the message box should animate as it opens and
2667                                    closes (defaults to undefined)
2668 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
2669                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
2670 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
2671                                    progress and wait dialogs will ignore this property and always hide the
2672                                    close button as they can only be closed programmatically.
2673 cls               String           A custom CSS class to apply to the message box element
2674 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
2675                                    displayed (defaults to 75)
2676 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
2677                                    function will be btn (the name of the button that was clicked, if applicable,
2678                                    e.g. "ok"), and text (the value of the active text field, if applicable).
2679                                    Progress and wait dialogs will ignore this option since they do not respond to
2680                                    user actions and can only be closed programmatically, so any required function
2681                                    should be called by the same code after it closes the dialog.
2682 icon              String           A CSS class that provides a background image to be used as an icon for
2683                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
2684 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
2685 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
2686 modal             Boolean          False to allow user interaction with the page while the message box is
2687                                    displayed (defaults to true)
2688 msg               String           A string that will replace the existing message box body text (defaults
2689                                    to the XHTML-compliant non-breaking space character '&#160;')
2690 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
2691 progress          Boolean          True to display a progress bar (defaults to false)
2692 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
2693 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
2694 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
2695 title             String           The title text
2696 value             String           The string value to set into the active textbox element if displayed
2697 wait              Boolean          True to display a progress bar (defaults to false)
2698 width             Number           The width of the dialog in pixels
2699 </pre>
2700          *
2701          * Example usage:
2702          * <pre><code>
2703 Roo.Msg.show({
2704    title: 'Address',
2705    msg: 'Please enter your address:',
2706    width: 300,
2707    buttons: Roo.MessageBox.OKCANCEL,
2708    multiline: true,
2709    fn: saveAddress,
2710    animEl: 'addAddressBtn'
2711 });
2712 </code></pre>
2713          * @param {Object} config Configuration options
2714          * @return {Roo.MessageBox} This message box
2715          */
2716         show : function(options)
2717         {
2718             
2719             // this causes nightmares if you show one dialog after another
2720             // especially on callbacks..
2721              
2722             if(this.isVisible()){
2723                 
2724                 this.hide();
2725                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
2726                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
2727                 Roo.log("New Dialog Message:" +  options.msg )
2728                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
2729                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
2730                 
2731             }
2732             var d = this.getDialog();
2733             opt = options;
2734             d.setTitle(opt.title || "&#160;");
2735             d.close.setDisplayed(opt.closable !== false);
2736             activeTextEl = textboxEl;
2737             opt.prompt = opt.prompt || (opt.multiline ? true : false);
2738             if(opt.prompt){
2739                 if(opt.multiline){
2740                     textboxEl.hide();
2741                     textareaEl.show();
2742                     textareaEl.setHeight(typeof opt.multiline == "number" ?
2743                         opt.multiline : this.defaultTextHeight);
2744                     activeTextEl = textareaEl;
2745                 }else{
2746                     textboxEl.show();
2747                     textareaEl.hide();
2748                 }
2749             }else{
2750                 textboxEl.hide();
2751                 textareaEl.hide();
2752             }
2753             progressEl.setDisplayed(opt.progress === true);
2754             this.updateProgress(0);
2755             activeTextEl.dom.value = opt.value || "";
2756             if(opt.prompt){
2757                 dlg.setDefaultButton(activeTextEl);
2758             }else{
2759                 var bs = opt.buttons;
2760                 var db = null;
2761                 if(bs && bs.ok){
2762                     db = buttons["ok"];
2763                 }else if(bs && bs.yes){
2764                     db = buttons["yes"];
2765                 }
2766                 dlg.setDefaultButton(db);
2767             }
2768             bwidth = updateButtons(opt.buttons);
2769             this.updateText(opt.msg);
2770             if(opt.cls){
2771                 d.el.addClass(opt.cls);
2772             }
2773             d.proxyDrag = opt.proxyDrag === true;
2774             d.modal = opt.modal !== false;
2775             d.mask = opt.modal !== false ? mask : false;
2776             if(!d.isVisible()){
2777                 // force it to the end of the z-index stack so it gets a cursor in FF
2778                 document.body.appendChild(dlg.el.dom);
2779                 d.animateTarget = null;
2780                 d.show(options.animEl);
2781             }
2782             return this;
2783         },
2784
2785         /**
2786          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
2787          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
2788          * and closing the message box when the process is complete.
2789          * @param {String} title The title bar text
2790          * @param {String} msg The message box body text
2791          * @return {Roo.MessageBox} This message box
2792          */
2793         progress : function(title, msg){
2794             this.show({
2795                 title : title,
2796                 msg : msg,
2797                 buttons: false,
2798                 progress:true,
2799                 closable:false,
2800                 minWidth: this.minProgressWidth,
2801                 modal : true
2802             });
2803             return this;
2804         },
2805
2806         /**
2807          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
2808          * If a callback function is passed it will be called after the user clicks the button, and the
2809          * id of the button that was clicked will be passed as the only parameter to the callback
2810          * (could also be the top-right close button).
2811          * @param {String} title The title bar text
2812          * @param {String} msg The message box body text
2813          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2814          * @param {Object} scope (optional) The scope of the callback function
2815          * @return {Roo.MessageBox} This message box
2816          */
2817         alert : function(title, msg, fn, scope){
2818             this.show({
2819                 title : title,
2820                 msg : msg,
2821                 buttons: this.OK,
2822                 fn: fn,
2823                 scope : scope,
2824                 modal : true
2825             });
2826             return this;
2827         },
2828
2829         /**
2830          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
2831          * interaction while waiting for a long-running process to complete that does not have defined intervals.
2832          * You are responsible for closing the message box when the process is complete.
2833          * @param {String} msg The message box body text
2834          * @param {String} title (optional) The title bar text
2835          * @return {Roo.MessageBox} This message box
2836          */
2837         wait : function(msg, title){
2838             this.show({
2839                 title : title,
2840                 msg : msg,
2841                 buttons: false,
2842                 closable:false,
2843                 progress:true,
2844                 modal:true,
2845                 width:300,
2846                 wait:true
2847             });
2848             waitTimer = Roo.TaskMgr.start({
2849                 run: function(i){
2850                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
2851                 },
2852                 interval: 1000
2853             });
2854             return this;
2855         },
2856
2857         /**
2858          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
2859          * If a callback function is passed it will be called after the user clicks either button, and the id of the
2860          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
2861          * @param {String} title The title bar text
2862          * @param {String} msg The message box body text
2863          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2864          * @param {Object} scope (optional) The scope of the callback function
2865          * @return {Roo.MessageBox} This message box
2866          */
2867         confirm : function(title, msg, fn, scope){
2868             this.show({
2869                 title : title,
2870                 msg : msg,
2871                 buttons: this.YESNO,
2872                 fn: fn,
2873                 scope : scope,
2874                 modal : true
2875             });
2876             return this;
2877         },
2878
2879         /**
2880          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
2881          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
2882          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
2883          * (could also be the top-right close button) and the text that was entered will be passed as the two
2884          * parameters to the callback.
2885          * @param {String} title The title bar text
2886          * @param {String} msg The message box body text
2887          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2888          * @param {Object} scope (optional) The scope of the callback function
2889          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
2890          * property, or the height in pixels to create the textbox (defaults to false / single-line)
2891          * @return {Roo.MessageBox} This message box
2892          */
2893         prompt : function(title, msg, fn, scope, multiline){
2894             this.show({
2895                 title : title,
2896                 msg : msg,
2897                 buttons: this.OKCANCEL,
2898                 fn: fn,
2899                 minWidth:250,
2900                 scope : scope,
2901                 prompt:true,
2902                 multiline: multiline,
2903                 modal : true
2904             });
2905             return this;
2906         },
2907
2908         /**
2909          * Button config that displays a single OK button
2910          * @type Object
2911          */
2912         OK : {ok:true},
2913         /**
2914          * Button config that displays Yes and No buttons
2915          * @type Object
2916          */
2917         YESNO : {yes:true, no:true},
2918         /**
2919          * Button config that displays OK and Cancel buttons
2920          * @type Object
2921          */
2922         OKCANCEL : {ok:true, cancel:true},
2923         /**
2924          * Button config that displays Yes, No and Cancel buttons
2925          * @type Object
2926          */
2927         YESNOCANCEL : {yes:true, no:true, cancel:true},
2928
2929         /**
2930          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
2931          * @type Number
2932          */
2933         defaultTextHeight : 75,
2934         /**
2935          * The maximum width in pixels of the message box (defaults to 600)
2936          * @type Number
2937          */
2938         maxWidth : 600,
2939         /**
2940          * The minimum width in pixels of the message box (defaults to 100)
2941          * @type Number
2942          */
2943         minWidth : 100,
2944         /**
2945          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
2946          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
2947          * @type Number
2948          */
2949         minProgressWidth : 250,
2950         /**
2951          * An object containing the default button text strings that can be overriden for localized language support.
2952          * Supported properties are: ok, cancel, yes and no.
2953          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
2954          * @type Object
2955          */
2956         buttonText : {
2957             ok : "OK",
2958             cancel : "Cancel",
2959             yes : "Yes",
2960             no : "No"
2961         }
2962     };
2963 }();
2964
2965 /**
2966  * Shorthand for {@link Roo.MessageBox}
2967  */
2968 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox 
2969 Roo.Msg = Roo.Msg || Roo.MessageBox;
2970 /*
2971  * - LGPL
2972  *
2973  * navbar
2974  * 
2975  */
2976
2977 /**
2978  * @class Roo.bootstrap.Navbar
2979  * @extends Roo.bootstrap.Component
2980  * Bootstrap Navbar class
2981
2982  * @constructor
2983  * Create a new Navbar
2984  * @param {Object} config The config object
2985  */
2986
2987
2988 Roo.bootstrap.Navbar = function(config){
2989     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
2990     
2991 };
2992
2993 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
2994     
2995     
2996    
2997     // private
2998     navItems : false,
2999     loadMask : false,
3000     
3001     
3002     getAutoCreate : function(){
3003         
3004         
3005         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3006         
3007     },
3008     
3009     initEvents :function ()
3010     {
3011         //Roo.log(this.el.select('.navbar-toggle',true));
3012         this.el.select('.navbar-toggle',true).on('click', function() {
3013            // Roo.log('click');
3014             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3015         }, this);
3016         
3017         var mark = {
3018             tag: "div",
3019             cls:"x-dlg-mask"
3020         }
3021         
3022         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3023         
3024         var size = this.el.getSize();
3025         this.maskEl.setSize(size.width, size.height);
3026         this.maskEl.enableDisplayMode("block");
3027         this.maskEl.hide();
3028         
3029         if(this.loadMask){
3030             this.maskEl.show();
3031         }
3032     },
3033     
3034     
3035     getChildContainer : function()
3036     {
3037         if (this.el.select('.collapse').getCount()) {
3038             return this.el.select('.collapse',true).first();
3039         }
3040         
3041         return this.el;
3042     },
3043     
3044     mask : function()
3045     {
3046         this.maskEl.show();
3047     },
3048     
3049     unmask : function()
3050     {
3051         this.maskEl.hide();
3052     } 
3053     
3054     
3055     
3056     
3057 });
3058
3059
3060
3061  
3062
3063  /*
3064  * - LGPL
3065  *
3066  * navbar
3067  * 
3068  */
3069
3070 /**
3071  * @class Roo.bootstrap.NavSimplebar
3072  * @extends Roo.bootstrap.Navbar
3073  * Bootstrap Sidebar class
3074  *
3075  * @cfg {Boolean} inverse is inverted color
3076  * 
3077  * @cfg {String} type (nav | pills | tabs)
3078  * @cfg {Boolean} arrangement stacked | justified
3079  * @cfg {String} align (left | right) alignment
3080  * 
3081  * @cfg {Boolean} main (true|false) main nav bar? default false
3082  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3083  * 
3084  * @cfg {String} tag (header|footer|nav|div) default is nav 
3085
3086  * 
3087  * 
3088  * 
3089  * @constructor
3090  * Create a new Sidebar
3091  * @param {Object} config The config object
3092  */
3093
3094
3095 Roo.bootstrap.NavSimplebar = function(config){
3096     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3097 };
3098
3099 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3100     
3101     inverse: false,
3102     
3103     type: false,
3104     arrangement: '',
3105     align : false,
3106     
3107     
3108     
3109     main : false,
3110     
3111     
3112     tag : false,
3113     
3114     
3115     getAutoCreate : function(){
3116         
3117         
3118         var cfg = {
3119             tag : this.tag || 'div',
3120             cls : 'navbar'
3121         };
3122           
3123         
3124         cfg.cn = [
3125             {
3126                 cls: 'nav',
3127                 tag : 'ul'
3128             }
3129         ];
3130         
3131          
3132         this.type = this.type || 'nav';
3133         if (['tabs','pills'].indexOf(this.type)!==-1) {
3134             cfg.cn[0].cls += ' nav-' + this.type
3135         
3136         
3137         } else {
3138             if (this.type!=='nav') {
3139                 Roo.log('nav type must be nav/tabs/pills')
3140             }
3141             cfg.cn[0].cls += ' navbar-nav'
3142         }
3143         
3144         
3145         
3146         
3147         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3148             cfg.cn[0].cls += ' nav-' + this.arrangement;
3149         }
3150         
3151         
3152         if (this.align === 'right') {
3153             cfg.cn[0].cls += ' navbar-right';
3154         }
3155         
3156         if (this.inverse) {
3157             cfg.cls += ' navbar-inverse';
3158             
3159         }
3160         
3161         
3162         return cfg;
3163     
3164         
3165     }
3166     
3167     
3168     
3169 });
3170
3171
3172
3173  
3174
3175  
3176        /*
3177  * - LGPL
3178  *
3179  * navbar
3180  * 
3181  */
3182
3183 /**
3184  * @class Roo.bootstrap.NavHeaderbar
3185  * @extends Roo.bootstrap.NavSimplebar
3186  * Bootstrap Sidebar class
3187  *
3188  * @cfg {String} brand what is brand
3189  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3190  * @cfg {String} brand_href href of the brand
3191  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3192  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3193  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3194  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3195  * 
3196  * @constructor
3197  * Create a new Sidebar
3198  * @param {Object} config The config object
3199  */
3200
3201
3202 Roo.bootstrap.NavHeaderbar = function(config){
3203     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3204       
3205 };
3206
3207 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3208     
3209     position: '',
3210     brand: '',
3211     brand_href: false,
3212     srButton : true,
3213     autohide : false,
3214     desktopCenter : false,
3215    
3216     
3217     getAutoCreate : function(){
3218         
3219         var   cfg = {
3220             tag: this.nav || 'nav',
3221             cls: 'navbar',
3222             role: 'navigation',
3223             cn: []
3224         };
3225         
3226         var cn = cfg.cn;
3227         if (this.desktopCenter) {
3228             cn.push({cls : 'container', cn : []});
3229             cn = cn[0].cn;
3230         }
3231         
3232         if(this.srButton){
3233             cn.push({
3234                 tag: 'div',
3235                 cls: 'navbar-header',
3236                 cn: [
3237                     {
3238                         tag: 'button',
3239                         type: 'button',
3240                         cls: 'navbar-toggle',
3241                         'data-toggle': 'collapse',
3242                         cn: [
3243                             {
3244                                 tag: 'span',
3245                                 cls: 'sr-only',
3246                                 html: 'Toggle navigation'
3247                             },
3248                             {
3249                                 tag: 'span',
3250                                 cls: 'icon-bar'
3251                             },
3252                             {
3253                                 tag: 'span',
3254                                 cls: 'icon-bar'
3255                             },
3256                             {
3257                                 tag: 'span',
3258                                 cls: 'icon-bar'
3259                             }
3260                         ]
3261                     }
3262                 ]
3263             });
3264         }
3265         
3266         cn.push({
3267             tag: 'div',
3268             cls: 'collapse navbar-collapse',
3269             cn : []
3270         });
3271         
3272         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3273         
3274         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3275             cfg.cls += ' navbar-' + this.position;
3276             
3277             // tag can override this..
3278             
3279             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3280         }
3281         
3282         if (this.brand !== '') {
3283             cn[0].cn.push({
3284                 tag: 'a',
3285                 href: this.brand_href ? this.brand_href : '#',
3286                 cls: 'navbar-brand',
3287                 cn: [
3288                 this.brand
3289                 ]
3290             });
3291         }
3292         
3293         if(this.main){
3294             cfg.cls += ' main-nav';
3295         }
3296         
3297         
3298         return cfg;
3299
3300         
3301     },
3302     getHeaderChildContainer : function()
3303     {
3304         if (this.el.select('.navbar-header').getCount()) {
3305             return this.el.select('.navbar-header',true).first();
3306         }
3307         
3308         return this.getChildContainer();
3309     },
3310     
3311     
3312     initEvents : function()
3313     {
3314         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3315         
3316         if (this.autohide) {
3317             
3318             var prevScroll = 0;
3319             var ft = this.el;
3320             
3321             Roo.get(document).on('scroll',function(e) {
3322                 var ns = Roo.get(document).getScroll().top;
3323                 var os = prevScroll;
3324                 prevScroll = ns;
3325                 
3326                 if(ns > os){
3327                     ft.removeClass('slideDown');
3328                     ft.addClass('slideUp');
3329                     return;
3330                 }
3331                 ft.removeClass('slideUp');
3332                 ft.addClass('slideDown');
3333                  
3334               
3335           },this);
3336         }
3337     }    
3338           
3339       
3340     
3341     
3342 });
3343
3344
3345
3346  
3347
3348  /*
3349  * - LGPL
3350  *
3351  * navbar
3352  * 
3353  */
3354
3355 /**
3356  * @class Roo.bootstrap.NavSidebar
3357  * @extends Roo.bootstrap.Navbar
3358  * Bootstrap Sidebar class
3359  * 
3360  * @constructor
3361  * Create a new Sidebar
3362  * @param {Object} config The config object
3363  */
3364
3365
3366 Roo.bootstrap.NavSidebar = function(config){
3367     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3368 };
3369
3370 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3371     
3372     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3373     
3374     getAutoCreate : function(){
3375         
3376         
3377         return  {
3378             tag: 'div',
3379             cls: 'sidebar sidebar-nav'
3380         };
3381     
3382         
3383     }
3384     
3385     
3386     
3387 });
3388
3389
3390
3391  
3392
3393  /*
3394  * - LGPL
3395  *
3396  * nav group
3397  * 
3398  */
3399
3400 /**
3401  * @class Roo.bootstrap.NavGroup
3402  * @extends Roo.bootstrap.Component
3403  * Bootstrap NavGroup class
3404  * @cfg {String} align left | right
3405  * @cfg {Boolean} inverse false | true
3406  * @cfg {String} type (nav|pills|tab) default nav
3407  * @cfg {String} navId - reference Id for navbar.
3408
3409  * 
3410  * @constructor
3411  * Create a new nav group
3412  * @param {Object} config The config object
3413  */
3414
3415 Roo.bootstrap.NavGroup = function(config){
3416     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3417     this.navItems = [];
3418    
3419     Roo.bootstrap.NavGroup.register(this);
3420      this.addEvents({
3421         /**
3422              * @event changed
3423              * Fires when the active item changes
3424              * @param {Roo.bootstrap.NavGroup} this
3425              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3426              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3427          */
3428         'changed': true
3429      });
3430     
3431 };
3432
3433 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3434     
3435     align: '',
3436     inverse: false,
3437     form: false,
3438     type: 'nav',
3439     navId : '',
3440     // private
3441     
3442     navItems : false, 
3443     
3444     getAutoCreate : function()
3445     {
3446         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3447         
3448         cfg = {
3449             tag : 'ul',
3450             cls: 'nav' 
3451         }
3452         
3453         if (['tabs','pills'].indexOf(this.type)!==-1) {
3454             cfg.cls += ' nav-' + this.type
3455         } else {
3456             if (this.type!=='nav') {
3457                 Roo.log('nav type must be nav/tabs/pills')
3458             }
3459             cfg.cls += ' navbar-nav'
3460         }
3461         
3462         if (this.parent().sidebar) {
3463             cfg = {
3464                 tag: 'ul',
3465                 cls: 'dashboard-menu sidebar-menu'
3466             }
3467             
3468             return cfg;
3469         }
3470         
3471         if (this.form === true) {
3472             cfg = {
3473                 tag: 'form',
3474                 cls: 'navbar-form'
3475             }
3476             
3477             if (this.align === 'right') {
3478                 cfg.cls += ' navbar-right';
3479             } else {
3480                 cfg.cls += ' navbar-left';
3481             }
3482         }
3483         
3484         if (this.align === 'right') {
3485             cfg.cls += ' navbar-right';
3486         }
3487         
3488         if (this.inverse) {
3489             cfg.cls += ' navbar-inverse';
3490             
3491         }
3492         
3493         
3494         return cfg;
3495     },
3496     /**
3497     * sets the active Navigation item
3498     * @param {Roo.bootstrap.NavItem} the new current navitem
3499     */
3500     setActiveItem : function(item)
3501     {
3502         var prev = false;
3503         Roo.each(this.navItems, function(v){
3504             if (v == item) {
3505                 return ;
3506             }
3507             if (v.isActive()) {
3508                 v.setActive(false, true);
3509                 prev = v;
3510                 
3511             }
3512             
3513         });
3514
3515         item.setActive(true, true);
3516         this.fireEvent('changed', this, item, prev);
3517         
3518         
3519     },
3520     /**
3521     * gets the active Navigation item
3522     * @return {Roo.bootstrap.NavItem} the current navitem
3523     */
3524     getActive : function()
3525     {
3526         
3527         var prev = false;
3528         Roo.each(this.navItems, function(v){
3529             
3530             if (v.isActive()) {
3531                 prev = v;
3532                 
3533             }
3534             
3535         });
3536         return prev;
3537     },
3538     
3539     indexOfNav : function()
3540     {
3541         
3542         var prev = false;
3543         Roo.each(this.navItems, function(v,i){
3544             
3545             if (v.isActive()) {
3546                 prev = i;
3547                 
3548             }
3549             
3550         });
3551         return prev;
3552     },
3553     /**
3554     * adds a Navigation item
3555     * @param {Roo.bootstrap.NavItem} the navitem to add
3556     */
3557     addItem : function(cfg)
3558     {
3559         var cn = new Roo.bootstrap.NavItem(cfg);
3560         this.register(cn);
3561         cn.parentId = this.id;
3562         cn.onRender(this.el, null);
3563         return cn;
3564     },
3565     /**
3566     * register a Navigation item
3567     * @param {Roo.bootstrap.NavItem} the navitem to add
3568     */
3569     register : function(item)
3570     {
3571         this.navItems.push( item);
3572         item.navId = this.navId;
3573     
3574     },
3575     
3576     /**
3577     * clear all the Navigation item
3578     */
3579    
3580     clearAll : function()
3581     {
3582         this.navItems = [];
3583         this.el.dom.innerHTML = '';
3584     },
3585     
3586     getNavItem: function(tabId)
3587     {
3588         var ret = false;
3589         Roo.each(this.navItems, function(e) {
3590             if (e.tabId == tabId) {
3591                ret =  e;
3592                return false;
3593             }
3594             return true;
3595             
3596         });
3597         return ret;
3598     },
3599     
3600     setActiveNext : function()
3601     {
3602         var i = this.indexOfNav(this.getActive());
3603         if (i > this.navItems.length) {
3604             return;
3605         }
3606         this.setActiveItem(this.navItems[i+1]);
3607     },
3608     setActivePrev : function()
3609     {
3610         var i = this.indexOfNav(this.getActive());
3611         if (i  < 1) {
3612             return;
3613         }
3614         this.setActiveItem(this.navItems[i-1]);
3615     },
3616     clearWasActive : function(except) {
3617         Roo.each(this.navItems, function(e) {
3618             if (e.tabId != except.tabId && e.was_active) {
3619                e.was_active = false;
3620                return false;
3621             }
3622             return true;
3623             
3624         });
3625     },
3626     getWasActive : function ()
3627     {
3628         var r = false;
3629         Roo.each(this.navItems, function(e) {
3630             if (e.was_active) {
3631                r = e;
3632                return false;
3633             }
3634             return true;
3635             
3636         });
3637         return r;
3638     }
3639     
3640     
3641 });
3642
3643  
3644 Roo.apply(Roo.bootstrap.NavGroup, {
3645     
3646     groups: {},
3647      /**
3648     * register a Navigation Group
3649     * @param {Roo.bootstrap.NavGroup} the navgroup to add
3650     */
3651     register : function(navgrp)
3652     {
3653         this.groups[navgrp.navId] = navgrp;
3654         
3655     },
3656     /**
3657     * fetch a Navigation Group based on the navigation ID
3658     * @param {string} the navgroup to add
3659     * @returns {Roo.bootstrap.NavGroup} the navgroup 
3660     */
3661     get: function(navId) {
3662         if (typeof(this.groups[navId]) == 'undefined') {
3663             return false;
3664             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
3665         }
3666         return this.groups[navId] ;
3667     }
3668     
3669     
3670     
3671 });
3672
3673  /*
3674  * - LGPL
3675  *
3676  * row
3677  * 
3678  */
3679
3680 /**
3681  * @class Roo.bootstrap.NavItem
3682  * @extends Roo.bootstrap.Component
3683  * Bootstrap Navbar.NavItem class
3684  * @cfg {String} href  link to
3685  * @cfg {String} html content of button
3686  * @cfg {String} badge text inside badge
3687  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
3688  * @cfg {String} glyphicon name of glyphicon
3689  * @cfg {String} icon name of font awesome icon
3690  * @cfg {Boolean} active Is item active
3691  * @cfg {Boolean} disabled Is item disabled
3692  
3693  * @cfg {Boolean} preventDefault (true | false) default false
3694  * @cfg {String} tabId the tab that this item activates.
3695  * @cfg {String} tagtype (a|span) render as a href or span?
3696   
3697  * @constructor
3698  * Create a new Navbar Item
3699  * @param {Object} config The config object
3700  */
3701 Roo.bootstrap.NavItem = function(config){
3702     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
3703     this.addEvents({
3704         // raw events
3705         /**
3706          * @event click
3707          * The raw click event for the entire grid.
3708          * @param {Roo.EventObject} e
3709          */
3710         "click" : true,
3711          /**
3712             * @event changed
3713             * Fires when the active item active state changes
3714             * @param {Roo.bootstrap.NavItem} this
3715             * @param {boolean} state the new state
3716              
3717          */
3718         'changed': true
3719     });
3720    
3721 };
3722
3723 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
3724     
3725     href: false,
3726     html: '',
3727     badge: '',
3728     icon: false,
3729     glyphicon: false,
3730     active: false,
3731     preventDefault : false,
3732     tabId : false,
3733     tagtype : 'a',
3734     disabled : false,
3735     
3736     was_active : false,
3737     
3738     getAutoCreate : function(){
3739          
3740         var cfg = {
3741             tag: 'li',
3742             cls: 'nav-item'
3743             
3744         }
3745         if (this.active) {
3746             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
3747         }
3748         if (this.disabled) {
3749             cfg.cls += ' disabled';
3750         }
3751         
3752         if (this.href || this.html || this.glyphicon || this.icon) {
3753             cfg.cn = [
3754                 {
3755                     tag: this.tagtype,
3756                     href : this.href || "#",
3757                     html: this.html || ''
3758                 }
3759             ];
3760             
3761             if (this.icon) {
3762                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
3763             }
3764
3765             if(this.glyphicon) {
3766                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
3767             }
3768             
3769             if (this.menu) {
3770                 
3771                 cfg.cn[0].html += " <span class='caret'></span>";
3772              
3773             }
3774             
3775             if (this.badge !== '') {
3776                  
3777                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
3778             }
3779         }
3780         
3781         
3782         
3783         return cfg;
3784     },
3785     initEvents: function() 
3786     {
3787         if (typeof (this.menu) != 'undefined') {
3788             this.menu.parentType = this.xtype;
3789             this.menu.triggerEl = this.el;
3790             this.menu = this.addxtype(Roo.apply({}, this.menu));
3791         }
3792         
3793         this.el.select('a',true).on('click', this.onClick, this);
3794         
3795         if(this.tagtype == 'span'){
3796             this.el.select('span',true).on('click', this.onClick, this);
3797         }
3798        
3799         // at this point parent should be available..
3800         this.parent().register(this);
3801     },
3802     
3803     onClick : function(e)
3804     {
3805          
3806         if(this.preventDefault){
3807             e.preventDefault();
3808         }
3809         if (this.disabled) {
3810             return;
3811         }
3812         
3813         var tg = Roo.bootstrap.TabGroup.get(this.navId);
3814         if (tg && tg.transition) {
3815             Roo.log("waiting for the transitionend");
3816             return;
3817         }
3818         
3819         Roo.log("fire event clicked");
3820         if(this.fireEvent('click', this, e) === false){
3821             return;
3822         };
3823         
3824         if(this.tagtype == 'span'){
3825             return;
3826         }
3827         
3828         var p = this.parent();
3829         if (['tabs','pills'].indexOf(p.type)!==-1) {
3830             if (typeof(p.setActiveItem) !== 'undefined') {
3831                 p.setActiveItem(this);
3832             }
3833         }
3834         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
3835         if (p.parentType == 'NavHeaderbar' && !this.menu) {
3836             // remove the collapsed menu expand...
3837             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
3838         }
3839         
3840     },
3841     
3842     isActive: function () {
3843         return this.active
3844     },
3845     setActive : function(state, fire, is_was_active)
3846     {
3847         if (this.active && !state & this.navId) {
3848             this.was_active = true;
3849             var nv = Roo.bootstrap.NavGroup.get(this.navId);
3850             if (nv) {
3851                 nv.clearWasActive(this);
3852             }
3853             
3854         }
3855         this.active = state;
3856         
3857         if (!state ) {
3858             this.el.removeClass('active');
3859         } else if (!this.el.hasClass('active')) {
3860             this.el.addClass('active');
3861         }
3862         if (fire) {
3863             this.fireEvent('changed', this, state);
3864         }
3865         
3866         // show a panel if it's registered and related..
3867         
3868         if (!this.navId || !this.tabId || !state || is_was_active) {
3869             return;
3870         }
3871         
3872         var tg = Roo.bootstrap.TabGroup.get(this.navId);
3873         if (!tg) {
3874             return;
3875         }
3876         var pan = tg.getPanelByName(this.tabId);
3877         if (!pan) {
3878             return;
3879         }
3880         // if we can not flip to new panel - go back to old nav highlight..
3881         if (false == tg.showPanel(pan)) {
3882             var nv = Roo.bootstrap.NavGroup.get(this.navId);
3883             if (nv) {
3884                 var onav = nv.getWasActive();
3885                 if (onav) {
3886                     onav.setActive(true, false, true);
3887                 }
3888             }
3889             
3890         }
3891         
3892         
3893         
3894     },
3895      // this should not be here...
3896     setDisabled : function(state)
3897     {
3898         this.disabled = state;
3899         if (!state ) {
3900             this.el.removeClass('disabled');
3901         } else if (!this.el.hasClass('disabled')) {
3902             this.el.addClass('disabled');
3903         }
3904         
3905     },
3906     
3907     /**
3908      * Fetch the element to display the tooltip on.
3909      * @return {Roo.Element} defaults to this.el
3910      */
3911     tooltipEl : function()
3912     {
3913         return this.el.select('' + this.tagtype + '', true).first();
3914     }
3915 });
3916  
3917
3918  /*
3919  * - LGPL
3920  *
3921  * sidebar item
3922  *
3923  *  li
3924  *    <span> icon </span>
3925  *    <span> text </span>
3926  *    <span>badge </span>
3927  */
3928
3929 /**
3930  * @class Roo.bootstrap.NavSidebarItem
3931  * @extends Roo.bootstrap.NavItem
3932  * Bootstrap Navbar.NavSidebarItem class
3933  * @constructor
3934  * Create a new Navbar Button
3935  * @param {Object} config The config object
3936  */
3937 Roo.bootstrap.NavSidebarItem = function(config){
3938     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
3939     this.addEvents({
3940         // raw events
3941         /**
3942          * @event click
3943          * The raw click event for the entire grid.
3944          * @param {Roo.EventObject} e
3945          */
3946         "click" : true,
3947          /**
3948             * @event changed
3949             * Fires when the active item active state changes
3950             * @param {Roo.bootstrap.NavSidebarItem} this
3951             * @param {boolean} state the new state
3952              
3953          */
3954         'changed': true
3955     });
3956    
3957 };
3958
3959 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
3960     
3961     
3962     getAutoCreate : function(){
3963         
3964         
3965         var a = {
3966                 tag: 'a',
3967                 href : this.href || '#',
3968                 cls: '',
3969                 html : '',
3970                 cn : []
3971         };
3972         var cfg = {
3973             tag: 'li',
3974             cls: '',
3975             cn: [ a ]
3976         }
3977         var span = {
3978             tag: 'span',
3979             html : this.html || ''
3980         }
3981         
3982         
3983         if (this.active) {
3984             cfg.cls += ' active';
3985         }
3986         
3987         // left icon..
3988         if (this.glyphicon || this.icon) {
3989             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
3990             a.cn.push({ tag : 'i', cls : c }) ;
3991         }
3992         // html..
3993         a.cn.push(span);
3994         // then badge..
3995         if (this.badge !== '') {
3996             a.cn.push({ tag: 'span',  cls : 'badge pull-right ' + (this.badgecls || ''), html: this.badge }); 
3997         }
3998         // fi
3999         if (this.menu) {
4000             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4001             a.cls += 'dropdown-toggle treeview' ;
4002             
4003         }
4004         
4005         
4006         
4007         return cfg;
4008          
4009            
4010     }
4011    
4012      
4013  
4014 });
4015  
4016
4017  /*
4018  * - LGPL
4019  *
4020  * row
4021  * 
4022  */
4023
4024 /**
4025  * @class Roo.bootstrap.Row
4026  * @extends Roo.bootstrap.Component
4027  * Bootstrap Row class (contains columns...)
4028  * 
4029  * @constructor
4030  * Create a new Row
4031  * @param {Object} config The config object
4032  */
4033
4034 Roo.bootstrap.Row = function(config){
4035     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4036 };
4037
4038 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4039     
4040     getAutoCreate : function(){
4041        return {
4042             cls: 'row clearfix'
4043        };
4044     }
4045     
4046     
4047 });
4048
4049  
4050
4051  /*
4052  * - LGPL
4053  *
4054  * element
4055  * 
4056  */
4057
4058 /**
4059  * @class Roo.bootstrap.Element
4060  * @extends Roo.bootstrap.Component
4061  * Bootstrap Element class
4062  * @cfg {String} html contents of the element
4063  * @cfg {String} tag tag of the element
4064  * @cfg {String} cls class of the element
4065  * 
4066  * @constructor
4067  * Create a new Element
4068  * @param {Object} config The config object
4069  */
4070
4071 Roo.bootstrap.Element = function(config){
4072     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4073 };
4074
4075 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4076     
4077     tag: 'div',
4078     cls: '',
4079     html: '',
4080      
4081     
4082     getAutoCreate : function(){
4083         
4084         var cfg = {
4085             tag: this.tag,
4086             cls: this.cls,
4087             html: this.html
4088         }
4089         
4090         
4091         
4092         return cfg;
4093     }
4094    
4095 });
4096
4097  
4098
4099  /*
4100  * - LGPL
4101  *
4102  * pagination
4103  * 
4104  */
4105
4106 /**
4107  * @class Roo.bootstrap.Pagination
4108  * @extends Roo.bootstrap.Component
4109  * Bootstrap Pagination class
4110  * @cfg {String} size xs | sm | md | lg
4111  * @cfg {Boolean} inverse false | true
4112  * 
4113  * @constructor
4114  * Create a new Pagination
4115  * @param {Object} config The config object
4116  */
4117
4118 Roo.bootstrap.Pagination = function(config){
4119     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4120 };
4121
4122 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4123     
4124     cls: false,
4125     size: false,
4126     inverse: false,
4127     
4128     getAutoCreate : function(){
4129         var cfg = {
4130             tag: 'ul',
4131                 cls: 'pagination'
4132         };
4133         if (this.inverse) {
4134             cfg.cls += ' inverse';
4135         }
4136         if (this.html) {
4137             cfg.html=this.html;
4138         }
4139         if (this.cls) {
4140             cfg.cls += " " + this.cls;
4141         }
4142         return cfg;
4143     }
4144    
4145 });
4146
4147  
4148
4149  /*
4150  * - LGPL
4151  *
4152  * Pagination item
4153  * 
4154  */
4155
4156
4157 /**
4158  * @class Roo.bootstrap.PaginationItem
4159  * @extends Roo.bootstrap.Component
4160  * Bootstrap PaginationItem class
4161  * @cfg {String} html text
4162  * @cfg {String} href the link
4163  * @cfg {Boolean} preventDefault (true | false) default true
4164  * @cfg {Boolean} active (true | false) default false
4165  * @cfg {Boolean} disabled default false
4166  * 
4167  * 
4168  * @constructor
4169  * Create a new PaginationItem
4170  * @param {Object} config The config object
4171  */
4172
4173
4174 Roo.bootstrap.PaginationItem = function(config){
4175     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4176     this.addEvents({
4177         // raw events
4178         /**
4179          * @event click
4180          * The raw click event for the entire grid.
4181          * @param {Roo.EventObject} e
4182          */
4183         "click" : true
4184     });
4185 };
4186
4187 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4188     
4189     href : false,
4190     html : false,
4191     preventDefault: true,
4192     active : false,
4193     cls : false,
4194     disabled: false,
4195     
4196     getAutoCreate : function(){
4197         var cfg= {
4198             tag: 'li',
4199             cn: [
4200                 {
4201                     tag : 'a',
4202                     href : this.href ? this.href : '#',
4203                     html : this.html ? this.html : ''
4204                 }
4205             ]
4206         };
4207         
4208         if(this.cls){
4209             cfg.cls = this.cls;
4210         }
4211         
4212         if(this.disabled){
4213             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4214         }
4215         
4216         if(this.active){
4217             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4218         }
4219         
4220         return cfg;
4221     },
4222     
4223     initEvents: function() {
4224         
4225         this.el.on('click', this.onClick, this);
4226         
4227     },
4228     onClick : function(e)
4229     {
4230         Roo.log('PaginationItem on click ');
4231         if(this.preventDefault){
4232             e.preventDefault();
4233         }
4234         
4235         if(this.disabled){
4236             return;
4237         }
4238         
4239         this.fireEvent('click', this, e);
4240     }
4241    
4242 });
4243
4244  
4245
4246  /*
4247  * - LGPL
4248  *
4249  * slider
4250  * 
4251  */
4252
4253
4254 /**
4255  * @class Roo.bootstrap.Slider
4256  * @extends Roo.bootstrap.Component
4257  * Bootstrap Slider class
4258  *    
4259  * @constructor
4260  * Create a new Slider
4261  * @param {Object} config The config object
4262  */
4263
4264 Roo.bootstrap.Slider = function(config){
4265     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4266 };
4267
4268 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
4269     
4270     getAutoCreate : function(){
4271         
4272         var cfg = {
4273             tag: 'div',
4274             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4275             cn: [
4276                 {
4277                     tag: 'a',
4278                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
4279                 }
4280             ]
4281         }
4282         
4283         return cfg;
4284     }
4285    
4286 });
4287
4288  /*
4289  * Based on:
4290  * Ext JS Library 1.1.1
4291  * Copyright(c) 2006-2007, Ext JS, LLC.
4292  *
4293  * Originally Released Under LGPL - original licence link has changed is not relivant.
4294  *
4295  * Fork - LGPL
4296  * <script type="text/javascript">
4297  */
4298  
4299
4300 /**
4301  * @class Roo.grid.ColumnModel
4302  * @extends Roo.util.Observable
4303  * This is the default implementation of a ColumnModel used by the Grid. It defines
4304  * the columns in the grid.
4305  * <br>Usage:<br>
4306  <pre><code>
4307  var colModel = new Roo.grid.ColumnModel([
4308         {header: "Ticker", width: 60, sortable: true, locked: true},
4309         {header: "Company Name", width: 150, sortable: true},
4310         {header: "Market Cap.", width: 100, sortable: true},
4311         {header: "$ Sales", width: 100, sortable: true, renderer: money},
4312         {header: "Employees", width: 100, sortable: true, resizable: false}
4313  ]);
4314  </code></pre>
4315  * <p>
4316  
4317  * The config options listed for this class are options which may appear in each
4318  * individual column definition.
4319  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4320  * @constructor
4321  * @param {Object} config An Array of column config objects. See this class's
4322  * config objects for details.
4323 */
4324 Roo.grid.ColumnModel = function(config){
4325         /**
4326      * The config passed into the constructor
4327      */
4328     this.config = config;
4329     this.lookup = {};
4330
4331     // if no id, create one
4332     // if the column does not have a dataIndex mapping,
4333     // map it to the order it is in the config
4334     for(var i = 0, len = config.length; i < len; i++){
4335         var c = config[i];
4336         if(typeof c.dataIndex == "undefined"){
4337             c.dataIndex = i;
4338         }
4339         if(typeof c.renderer == "string"){
4340             c.renderer = Roo.util.Format[c.renderer];
4341         }
4342         if(typeof c.id == "undefined"){
4343             c.id = Roo.id();
4344         }
4345         if(c.editor && c.editor.xtype){
4346             c.editor  = Roo.factory(c.editor, Roo.grid);
4347         }
4348         if(c.editor && c.editor.isFormField){
4349             c.editor = new Roo.grid.GridEditor(c.editor);
4350         }
4351         this.lookup[c.id] = c;
4352     }
4353
4354     /**
4355      * The width of columns which have no width specified (defaults to 100)
4356      * @type Number
4357      */
4358     this.defaultWidth = 100;
4359
4360     /**
4361      * Default sortable of columns which have no sortable specified (defaults to false)
4362      * @type Boolean
4363      */
4364     this.defaultSortable = false;
4365
4366     this.addEvents({
4367         /**
4368              * @event widthchange
4369              * Fires when the width of a column changes.
4370              * @param {ColumnModel} this
4371              * @param {Number} columnIndex The column index
4372              * @param {Number} newWidth The new width
4373              */
4374             "widthchange": true,
4375         /**
4376              * @event headerchange
4377              * Fires when the text of a header changes.
4378              * @param {ColumnModel} this
4379              * @param {Number} columnIndex The column index
4380              * @param {Number} newText The new header text
4381              */
4382             "headerchange": true,
4383         /**
4384              * @event hiddenchange
4385              * Fires when a column is hidden or "unhidden".
4386              * @param {ColumnModel} this
4387              * @param {Number} columnIndex The column index
4388              * @param {Boolean} hidden true if hidden, false otherwise
4389              */
4390             "hiddenchange": true,
4391             /**
4392          * @event columnmoved
4393          * Fires when a column is moved.
4394          * @param {ColumnModel} this
4395          * @param {Number} oldIndex
4396          * @param {Number} newIndex
4397          */
4398         "columnmoved" : true,
4399         /**
4400          * @event columlockchange
4401          * Fires when a column's locked state is changed
4402          * @param {ColumnModel} this
4403          * @param {Number} colIndex
4404          * @param {Boolean} locked true if locked
4405          */
4406         "columnlockchange" : true
4407     });
4408     Roo.grid.ColumnModel.superclass.constructor.call(this);
4409 };
4410 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4411     /**
4412      * @cfg {String} header The header text to display in the Grid view.
4413      */
4414     /**
4415      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4416      * {@link Roo.data.Record} definition from which to draw the column's value. If not
4417      * specified, the column's index is used as an index into the Record's data Array.
4418      */
4419     /**
4420      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4421      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4422      */
4423     /**
4424      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4425      * Defaults to the value of the {@link #defaultSortable} property.
4426      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4427      */
4428     /**
4429      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
4430      */
4431     /**
4432      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
4433      */
4434     /**
4435      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4436      */
4437     /**
4438      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4439      */
4440     /**
4441      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4442      * given the cell's data value. See {@link #setRenderer}. If not specified, the
4443      * default renderer uses the raw data value. If an object is returned (bootstrap only)
4444      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4445      */
4446        /**
4447      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
4448      */
4449     /**
4450      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
4451      */
4452     /**
4453      * @cfg {String} cursor (Optional)
4454      */
4455     /**
4456      * Returns the id of the column at the specified index.
4457      * @param {Number} index The column index
4458      * @return {String} the id
4459      */
4460     getColumnId : function(index){
4461         return this.config[index].id;
4462     },
4463
4464     /**
4465      * Returns the column for a specified id.
4466      * @param {String} id The column id
4467      * @return {Object} the column
4468      */
4469     getColumnById : function(id){
4470         return this.lookup[id];
4471     },
4472
4473     
4474     /**
4475      * Returns the column for a specified dataIndex.
4476      * @param {String} dataIndex The column dataIndex
4477      * @return {Object|Boolean} the column or false if not found
4478      */
4479     getColumnByDataIndex: function(dataIndex){
4480         var index = this.findColumnIndex(dataIndex);
4481         return index > -1 ? this.config[index] : false;
4482     },
4483     
4484     /**
4485      * Returns the index for a specified column id.
4486      * @param {String} id The column id
4487      * @return {Number} the index, or -1 if not found
4488      */
4489     getIndexById : function(id){
4490         for(var i = 0, len = this.config.length; i < len; i++){
4491             if(this.config[i].id == id){
4492                 return i;
4493             }
4494         }
4495         return -1;
4496     },
4497     
4498     /**
4499      * Returns the index for a specified column dataIndex.
4500      * @param {String} dataIndex The column dataIndex
4501      * @return {Number} the index, or -1 if not found
4502      */
4503     
4504     findColumnIndex : function(dataIndex){
4505         for(var i = 0, len = this.config.length; i < len; i++){
4506             if(this.config[i].dataIndex == dataIndex){
4507                 return i;
4508             }
4509         }
4510         return -1;
4511     },
4512     
4513     
4514     moveColumn : function(oldIndex, newIndex){
4515         var c = this.config[oldIndex];
4516         this.config.splice(oldIndex, 1);
4517         this.config.splice(newIndex, 0, c);
4518         this.dataMap = null;
4519         this.fireEvent("columnmoved", this, oldIndex, newIndex);
4520     },
4521
4522     isLocked : function(colIndex){
4523         return this.config[colIndex].locked === true;
4524     },
4525
4526     setLocked : function(colIndex, value, suppressEvent){
4527         if(this.isLocked(colIndex) == value){
4528             return;
4529         }
4530         this.config[colIndex].locked = value;
4531         if(!suppressEvent){
4532             this.fireEvent("columnlockchange", this, colIndex, value);
4533         }
4534     },
4535
4536     getTotalLockedWidth : function(){
4537         var totalWidth = 0;
4538         for(var i = 0; i < this.config.length; i++){
4539             if(this.isLocked(i) && !this.isHidden(i)){
4540                 this.totalWidth += this.getColumnWidth(i);
4541             }
4542         }
4543         return totalWidth;
4544     },
4545
4546     getLockedCount : function(){
4547         for(var i = 0, len = this.config.length; i < len; i++){
4548             if(!this.isLocked(i)){
4549                 return i;
4550             }
4551         }
4552     },
4553
4554     /**
4555      * Returns the number of columns.
4556      * @return {Number}
4557      */
4558     getColumnCount : function(visibleOnly){
4559         if(visibleOnly === true){
4560             var c = 0;
4561             for(var i = 0, len = this.config.length; i < len; i++){
4562                 if(!this.isHidden(i)){
4563                     c++;
4564                 }
4565             }
4566             return c;
4567         }
4568         return this.config.length;
4569     },
4570
4571     /**
4572      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
4573      * @param {Function} fn
4574      * @param {Object} scope (optional)
4575      * @return {Array} result
4576      */
4577     getColumnsBy : function(fn, scope){
4578         var r = [];
4579         for(var i = 0, len = this.config.length; i < len; i++){
4580             var c = this.config[i];
4581             if(fn.call(scope||this, c, i) === true){
4582                 r[r.length] = c;
4583             }
4584         }
4585         return r;
4586     },
4587
4588     /**
4589      * Returns true if the specified column is sortable.
4590      * @param {Number} col The column index
4591      * @return {Boolean}
4592      */
4593     isSortable : function(col){
4594         if(typeof this.config[col].sortable == "undefined"){
4595             return this.defaultSortable;
4596         }
4597         return this.config[col].sortable;
4598     },
4599
4600     /**
4601      * Returns the rendering (formatting) function defined for the column.
4602      * @param {Number} col The column index.
4603      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
4604      */
4605     getRenderer : function(col){
4606         if(!this.config[col].renderer){
4607             return Roo.grid.ColumnModel.defaultRenderer;
4608         }
4609         return this.config[col].renderer;
4610     },
4611
4612     /**
4613      * Sets the rendering (formatting) function for a column.
4614      * @param {Number} col The column index
4615      * @param {Function} fn The function to use to process the cell's raw data
4616      * to return HTML markup for the grid view. The render function is called with
4617      * the following parameters:<ul>
4618      * <li>Data value.</li>
4619      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
4620      * <li>css A CSS style string to apply to the table cell.</li>
4621      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
4622      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
4623      * <li>Row index</li>
4624      * <li>Column index</li>
4625      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
4626      */
4627     setRenderer : function(col, fn){
4628         this.config[col].renderer = fn;
4629     },
4630
4631     /**
4632      * Returns the width for the specified column.
4633      * @param {Number} col The column index
4634      * @return {Number}
4635      */
4636     getColumnWidth : function(col){
4637         return this.config[col].width * 1 || this.defaultWidth;
4638     },
4639
4640     /**
4641      * Sets the width for a column.
4642      * @param {Number} col The column index
4643      * @param {Number} width The new width
4644      */
4645     setColumnWidth : function(col, width, suppressEvent){
4646         this.config[col].width = width;
4647         this.totalWidth = null;
4648         if(!suppressEvent){
4649              this.fireEvent("widthchange", this, col, width);
4650         }
4651     },
4652
4653     /**
4654      * Returns the total width of all columns.
4655      * @param {Boolean} includeHidden True to include hidden column widths
4656      * @return {Number}
4657      */
4658     getTotalWidth : function(includeHidden){
4659         if(!this.totalWidth){
4660             this.totalWidth = 0;
4661             for(var i = 0, len = this.config.length; i < len; i++){
4662                 if(includeHidden || !this.isHidden(i)){
4663                     this.totalWidth += this.getColumnWidth(i);
4664                 }
4665             }
4666         }
4667         return this.totalWidth;
4668     },
4669
4670     /**
4671      * Returns the header for the specified column.
4672      * @param {Number} col The column index
4673      * @return {String}
4674      */
4675     getColumnHeader : function(col){
4676         return this.config[col].header;
4677     },
4678
4679     /**
4680      * Sets the header for a column.
4681      * @param {Number} col The column index
4682      * @param {String} header The new header
4683      */
4684     setColumnHeader : function(col, header){
4685         this.config[col].header = header;
4686         this.fireEvent("headerchange", this, col, header);
4687     },
4688
4689     /**
4690      * Returns the tooltip for the specified column.
4691      * @param {Number} col The column index
4692      * @return {String}
4693      */
4694     getColumnTooltip : function(col){
4695             return this.config[col].tooltip;
4696     },
4697     /**
4698      * Sets the tooltip for a column.
4699      * @param {Number} col The column index
4700      * @param {String} tooltip The new tooltip
4701      */
4702     setColumnTooltip : function(col, tooltip){
4703             this.config[col].tooltip = tooltip;
4704     },
4705
4706     /**
4707      * Returns the dataIndex for the specified column.
4708      * @param {Number} col The column index
4709      * @return {Number}
4710      */
4711     getDataIndex : function(col){
4712         return this.config[col].dataIndex;
4713     },
4714
4715     /**
4716      * Sets the dataIndex for a column.
4717      * @param {Number} col The column index
4718      * @param {Number} dataIndex The new dataIndex
4719      */
4720     setDataIndex : function(col, dataIndex){
4721         this.config[col].dataIndex = dataIndex;
4722     },
4723
4724     
4725     
4726     /**
4727      * Returns true if the cell is editable.
4728      * @param {Number} colIndex The column index
4729      * @param {Number} rowIndex The row index
4730      * @return {Boolean}
4731      */
4732     isCellEditable : function(colIndex, rowIndex){
4733         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
4734     },
4735
4736     /**
4737      * Returns the editor defined for the cell/column.
4738      * return false or null to disable editing.
4739      * @param {Number} colIndex The column index
4740      * @param {Number} rowIndex The row index
4741      * @return {Object}
4742      */
4743     getCellEditor : function(colIndex, rowIndex){
4744         return this.config[colIndex].editor;
4745     },
4746
4747     /**
4748      * Sets if a column is editable.
4749      * @param {Number} col The column index
4750      * @param {Boolean} editable True if the column is editable
4751      */
4752     setEditable : function(col, editable){
4753         this.config[col].editable = editable;
4754     },
4755
4756
4757     /**
4758      * Returns true if the column is hidden.
4759      * @param {Number} colIndex The column index
4760      * @return {Boolean}
4761      */
4762     isHidden : function(colIndex){
4763         return this.config[colIndex].hidden;
4764     },
4765
4766
4767     /**
4768      * Returns true if the column width cannot be changed
4769      */
4770     isFixed : function(colIndex){
4771         return this.config[colIndex].fixed;
4772     },
4773
4774     /**
4775      * Returns true if the column can be resized
4776      * @return {Boolean}
4777      */
4778     isResizable : function(colIndex){
4779         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
4780     },
4781     /**
4782      * Sets if a column is hidden.
4783      * @param {Number} colIndex The column index
4784      * @param {Boolean} hidden True if the column is hidden
4785      */
4786     setHidden : function(colIndex, hidden){
4787         this.config[colIndex].hidden = hidden;
4788         this.totalWidth = null;
4789         this.fireEvent("hiddenchange", this, colIndex, hidden);
4790     },
4791
4792     /**
4793      * Sets the editor for a column.
4794      * @param {Number} col The column index
4795      * @param {Object} editor The editor object
4796      */
4797     setEditor : function(col, editor){
4798         this.config[col].editor = editor;
4799     }
4800 });
4801
4802 Roo.grid.ColumnModel.defaultRenderer = function(value){
4803         if(typeof value == "string" && value.length < 1){
4804             return "&#160;";
4805         }
4806         return value;
4807 };
4808
4809 // Alias for backwards compatibility
4810 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
4811 /*
4812  * Based on:
4813  * Ext JS Library 1.1.1
4814  * Copyright(c) 2006-2007, Ext JS, LLC.
4815  *
4816  * Originally Released Under LGPL - original licence link has changed is not relivant.
4817  *
4818  * Fork - LGPL
4819  * <script type="text/javascript">
4820  */
4821  
4822 /**
4823  * @class Roo.LoadMask
4824  * A simple utility class for generically masking elements while loading data.  If the element being masked has
4825  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
4826  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
4827  * element's UpdateManager load indicator and will be destroyed after the initial load.
4828  * @constructor
4829  * Create a new LoadMask
4830  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
4831  * @param {Object} config The config object
4832  */
4833 Roo.LoadMask = function(el, config){
4834     this.el = Roo.get(el);
4835     Roo.apply(this, config);
4836     if(this.store){
4837         this.store.on('beforeload', this.onBeforeLoad, this);
4838         this.store.on('load', this.onLoad, this);
4839         this.store.on('loadexception', this.onLoadException, this);
4840         this.removeMask = false;
4841     }else{
4842         var um = this.el.getUpdateManager();
4843         um.showLoadIndicator = false; // disable the default indicator
4844         um.on('beforeupdate', this.onBeforeLoad, this);
4845         um.on('update', this.onLoad, this);
4846         um.on('failure', this.onLoad, this);
4847         this.removeMask = true;
4848     }
4849 };
4850
4851 Roo.LoadMask.prototype = {
4852     /**
4853      * @cfg {Boolean} removeMask
4854      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
4855      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
4856      */
4857     /**
4858      * @cfg {String} msg
4859      * The text to display in a centered loading message box (defaults to 'Loading...')
4860      */
4861     msg : 'Loading...',
4862     /**
4863      * @cfg {String} msgCls
4864      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
4865      */
4866     msgCls : 'x-mask-loading',
4867
4868     /**
4869      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
4870      * @type Boolean
4871      */
4872     disabled: false,
4873
4874     /**
4875      * Disables the mask to prevent it from being displayed
4876      */
4877     disable : function(){
4878        this.disabled = true;
4879     },
4880
4881     /**
4882      * Enables the mask so that it can be displayed
4883      */
4884     enable : function(){
4885         this.disabled = false;
4886     },
4887     
4888     onLoadException : function()
4889     {
4890         Roo.log(arguments);
4891         
4892         if (typeof(arguments[3]) != 'undefined') {
4893             Roo.MessageBox.alert("Error loading",arguments[3]);
4894         } 
4895         /*
4896         try {
4897             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
4898                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
4899             }   
4900         } catch(e) {
4901             
4902         }
4903         */
4904     
4905         
4906         
4907         this.el.unmask(this.removeMask);
4908     },
4909     // private
4910     onLoad : function()
4911     {
4912         this.el.unmask(this.removeMask);
4913     },
4914
4915     // private
4916     onBeforeLoad : function(){
4917         if(!this.disabled){
4918             this.el.mask(this.msg, this.msgCls);
4919         }
4920     },
4921
4922     // private
4923     destroy : function(){
4924         if(this.store){
4925             this.store.un('beforeload', this.onBeforeLoad, this);
4926             this.store.un('load', this.onLoad, this);
4927             this.store.un('loadexception', this.onLoadException, this);
4928         }else{
4929             var um = this.el.getUpdateManager();
4930             um.un('beforeupdate', this.onBeforeLoad, this);
4931             um.un('update', this.onLoad, this);
4932             um.un('failure', this.onLoad, this);
4933         }
4934     }
4935 };/*
4936  * - LGPL
4937  *
4938  * table
4939  * 
4940  */
4941
4942 /**
4943  * @class Roo.bootstrap.Table
4944  * @extends Roo.bootstrap.Component
4945  * Bootstrap Table class
4946  * @cfg {String} cls table class
4947  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
4948  * @cfg {String} bgcolor Specifies the background color for a table
4949  * @cfg {Number} border Specifies whether the table cells should have borders or not
4950  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
4951  * @cfg {Number} cellspacing Specifies the space between cells
4952  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
4953  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
4954  * @cfg {String} sortable Specifies that the table should be sortable
4955  * @cfg {String} summary Specifies a summary of the content of a table
4956  * @cfg {Number} width Specifies the width of a table
4957  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
4958  * 
4959  * @cfg {boolean} striped Should the rows be alternative striped
4960  * @cfg {boolean} bordered Add borders to the table
4961  * @cfg {boolean} hover Add hover highlighting
4962  * @cfg {boolean} condensed Format condensed
4963  * @cfg {boolean} responsive Format condensed
4964  * @cfg {Boolean} loadMask (true|false) default false
4965  * @cfg {Boolean} tfoot (true|false) generate tfoot, default true
4966  * @cfg {Boolean} thead (true|false) generate thead, default true
4967  * @cfg {Boolean} RowSelection (true|false) default false
4968  * @cfg {Boolean} CellSelection (true|false) default false
4969  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
4970  
4971  * 
4972  * @constructor
4973  * Create a new Table
4974  * @param {Object} config The config object
4975  */
4976
4977 Roo.bootstrap.Table = function(config){
4978     Roo.bootstrap.Table.superclass.constructor.call(this, config);
4979     
4980     if (this.sm) {
4981         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
4982         this.sm = this.selModel;
4983         this.sm.xmodule = this.xmodule || false;
4984     }
4985     if (this.cm && typeof(this.cm.config) == 'undefined') {
4986         this.colModel = new Roo.grid.ColumnModel(this.cm);
4987         this.cm = this.colModel;
4988         this.cm.xmodule = this.xmodule || false;
4989     }
4990     if (this.store) {
4991         this.store= Roo.factory(this.store, Roo.data);
4992         this.ds = this.store;
4993         this.ds.xmodule = this.xmodule || false;
4994          
4995     }
4996     if (this.footer && this.store) {
4997         this.footer.dataSource = this.ds;
4998         this.footer = Roo.factory(this.footer);
4999     }
5000     
5001     /** @private */
5002     this.addEvents({
5003         /**
5004          * @event cellclick
5005          * Fires when a cell is clicked
5006          * @param {Roo.bootstrap.Table} this
5007          * @param {Roo.Element} el
5008          * @param {Number} rowIndex
5009          * @param {Number} columnIndex
5010          * @param {Roo.EventObject} e
5011          */
5012         "cellclick" : true,
5013         /**
5014          * @event celldblclick
5015          * Fires when a cell is double clicked
5016          * @param {Roo.bootstrap.Table} this
5017          * @param {Roo.Element} el
5018          * @param {Number} rowIndex
5019          * @param {Number} columnIndex
5020          * @param {Roo.EventObject} e
5021          */
5022         "celldblclick" : true,
5023         /**
5024          * @event rowclick
5025          * Fires when a row is clicked
5026          * @param {Roo.bootstrap.Table} this
5027          * @param {Roo.Element} el
5028          * @param {Number} rowIndex
5029          * @param {Roo.EventObject} e
5030          */
5031         "rowclick" : true,
5032         /**
5033          * @event rowdblclick
5034          * Fires when a row is double clicked
5035          * @param {Roo.bootstrap.Table} this
5036          * @param {Roo.Element} el
5037          * @param {Number} rowIndex
5038          * @param {Roo.EventObject} e
5039          */
5040         "rowdblclick" : true,
5041         /**
5042          * @event mouseover
5043          * Fires when a mouseover occur
5044          * @param {Roo.bootstrap.Table} this
5045          * @param {Roo.Element} el
5046          * @param {Number} rowIndex
5047          * @param {Number} columnIndex
5048          * @param {Roo.EventObject} e
5049          */
5050         "mouseover" : true,
5051         /**
5052          * @event mouseout
5053          * Fires when a mouseout occur
5054          * @param {Roo.bootstrap.Table} this
5055          * @param {Roo.Element} el
5056          * @param {Number} rowIndex
5057          * @param {Number} columnIndex
5058          * @param {Roo.EventObject} e
5059          */
5060         "mouseout" : true,
5061         /**
5062          * @event rowclass
5063          * Fires when a row is rendered, so you can change add a style to it.
5064          * @param {Roo.bootstrap.Table} this
5065          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5066          */
5067         'rowclass' : true
5068         
5069     });
5070 };
5071
5072 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5073     
5074     cls: false,
5075     align: false,
5076     bgcolor: false,
5077     border: false,
5078     cellpadding: false,
5079     cellspacing: false,
5080     frame: false,
5081     rules: false,
5082     sortable: false,
5083     summary: false,
5084     width: false,
5085     striped : false,
5086     bordered: false,
5087     hover:  false,
5088     condensed : false,
5089     responsive : false,
5090     sm : false,
5091     cm : false,
5092     store : false,
5093     loadMask : false,
5094     tfoot : true,
5095     thead : true,
5096     RowSelection : false,
5097     CellSelection : false,
5098     layout : false,
5099     
5100     // Roo.Element - the tbody
5101     mainBody: false, 
5102     
5103     getAutoCreate : function(){
5104         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5105         
5106         cfg = {
5107             tag: 'table',
5108             cls : 'table',
5109             cn : []
5110         }
5111             
5112         if (this.striped) {
5113             cfg.cls += ' table-striped';
5114         }
5115         
5116         if (this.hover) {
5117             cfg.cls += ' table-hover';
5118         }
5119         if (this.bordered) {
5120             cfg.cls += ' table-bordered';
5121         }
5122         if (this.condensed) {
5123             cfg.cls += ' table-condensed';
5124         }
5125         if (this.responsive) {
5126             cfg.cls += ' table-responsive';
5127         }
5128         
5129         if (this.cls) {
5130             cfg.cls+=  ' ' +this.cls;
5131         }
5132         
5133         // this lot should be simplifed...
5134         
5135         if (this.align) {
5136             cfg.align=this.align;
5137         }
5138         if (this.bgcolor) {
5139             cfg.bgcolor=this.bgcolor;
5140         }
5141         if (this.border) {
5142             cfg.border=this.border;
5143         }
5144         if (this.cellpadding) {
5145             cfg.cellpadding=this.cellpadding;
5146         }
5147         if (this.cellspacing) {
5148             cfg.cellspacing=this.cellspacing;
5149         }
5150         if (this.frame) {
5151             cfg.frame=this.frame;
5152         }
5153         if (this.rules) {
5154             cfg.rules=this.rules;
5155         }
5156         if (this.sortable) {
5157             cfg.sortable=this.sortable;
5158         }
5159         if (this.summary) {
5160             cfg.summary=this.summary;
5161         }
5162         if (this.width) {
5163             cfg.width=this.width;
5164         }
5165         if (this.layout) {
5166             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5167         }
5168         
5169         if(this.store || this.cm){
5170             if(this.thead){
5171                 cfg.cn.push(this.renderHeader());
5172             }
5173             
5174             cfg.cn.push(this.renderBody());
5175             
5176             if(this.tfoot){
5177                 cfg.cn.push(this.renderFooter());
5178             }
5179             
5180             cfg.cls+=  ' TableGrid';
5181         }
5182         
5183         return { cn : [ cfg ] };
5184     },
5185     
5186     initEvents : function()
5187     {   
5188         if(!this.store || !this.cm){
5189             return;
5190         }
5191         
5192         //Roo.log('initEvents with ds!!!!');
5193         
5194         this.mainBody = this.el.select('tbody', true).first();
5195         
5196         
5197         var _this = this;
5198         
5199         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5200             e.on('click', _this.sort, _this);
5201         });
5202         
5203         this.el.on("click", this.onClick, this);
5204         this.el.on("dblclick", this.onDblClick, this);
5205         
5206         // why is this done????? = it breaks dialogs??
5207         //this.parent().el.setStyle('position', 'relative');
5208         
5209         
5210         if (this.footer) {
5211             this.footer.parentId = this.id;
5212             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
5213         }
5214         
5215         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5216         
5217         this.store.on('load', this.onLoad, this);
5218         this.store.on('beforeload', this.onBeforeLoad, this);
5219         this.store.on('update', this.onUpdate, this);
5220         this.store.on('add', this.onAdd, this);
5221         
5222     },
5223     
5224     onMouseover : function(e, el)
5225     {
5226         var cell = Roo.get(el);
5227         
5228         if(!cell){
5229             return;
5230         }
5231         
5232         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5233             cell = cell.findParent('td', false, true);
5234         }
5235         
5236         var row = cell.findParent('tr', false, true);
5237         var cellIndex = cell.dom.cellIndex;
5238         var rowIndex = row.dom.rowIndex - 1; // start from 0
5239         
5240         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5241         
5242     },
5243     
5244     onMouseout : function(e, el)
5245     {
5246         var cell = Roo.get(el);
5247         
5248         if(!cell){
5249             return;
5250         }
5251         
5252         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5253             cell = cell.findParent('td', false, true);
5254         }
5255         
5256         var row = cell.findParent('tr', false, true);
5257         var cellIndex = cell.dom.cellIndex;
5258         var rowIndex = row.dom.rowIndex - 1; // start from 0
5259         
5260         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5261         
5262     },
5263     
5264     onClick : function(e, el)
5265     {
5266         var cell = Roo.get(el);
5267         
5268         if(!cell || (!this.CellSelection && !this.RowSelection)){
5269             return;
5270         }
5271         
5272         
5273         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5274             cell = cell.findParent('td', false, true);
5275         }
5276         
5277         var row = cell.findParent('tr', false, true);
5278         var cellIndex = cell.dom.cellIndex;
5279         var rowIndex = row.dom.rowIndex - 1;
5280         
5281         if(this.CellSelection){
5282             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5283         }
5284         
5285         if(this.RowSelection){
5286             this.fireEvent('rowclick', this, row, rowIndex, e);
5287         }
5288         
5289         
5290     },
5291     
5292     onDblClick : function(e,el)
5293     {
5294         var cell = Roo.get(el);
5295         
5296         if(!cell || (!this.CellSelection && !this.RowSelection)){
5297             return;
5298         }
5299         
5300         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5301             cell = cell.findParent('td', false, true);
5302         }
5303         
5304         var row = cell.findParent('tr', false, true);
5305         var cellIndex = cell.dom.cellIndex;
5306         var rowIndex = row.dom.rowIndex - 1;
5307         
5308         if(this.CellSelection){
5309             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5310         }
5311         
5312         if(this.RowSelection){
5313             this.fireEvent('rowdblclick', this, row, rowIndex, e);
5314         }
5315     },
5316     
5317     sort : function(e,el)
5318     {
5319         var col = Roo.get(el)
5320         
5321         if(!col.hasClass('sortable')){
5322             return;
5323         }
5324         
5325         var sort = col.attr('sort');
5326         var dir = 'ASC';
5327         
5328         if(col.hasClass('glyphicon-arrow-up')){
5329             dir = 'DESC';
5330         }
5331         
5332         this.store.sortInfo = {field : sort, direction : dir};
5333         
5334         if (this.footer) {
5335             Roo.log("calling footer first");
5336             this.footer.onClick('first');
5337         } else {
5338         
5339             this.store.load({ params : { start : 0 } });
5340         }
5341     },
5342     
5343     renderHeader : function()
5344     {
5345         var header = {
5346             tag: 'thead',
5347             cn : []
5348         };
5349         
5350         var cm = this.cm;
5351         
5352         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5353             
5354             var config = cm.config[i];
5355                     
5356             var c = {
5357                 tag: 'th',
5358                 style : '',
5359                 html: cm.getColumnHeader(i)
5360             };
5361             
5362             if(typeof(config.hidden) != 'undefined' && config.hidden){
5363                 c.style += ' display:none;';
5364             }
5365             
5366             if(typeof(config.dataIndex) != 'undefined'){
5367                 c.sort = config.dataIndex;
5368             }
5369             
5370             if(typeof(config.sortable) != 'undefined' && config.sortable){
5371                 c.cls = 'sortable';
5372             }
5373             
5374             if(typeof(config.align) != 'undefined' && config.align.length){
5375                 c.style += ' text-align:' + config.align + ';';
5376             }
5377             
5378             if(typeof(config.width) != 'undefined'){
5379                 c.style += ' width:' + config.width + 'px;';
5380             }
5381             
5382             header.cn.push(c)
5383         }
5384         
5385         return header;
5386     },
5387     
5388     renderBody : function()
5389     {
5390         var body = {
5391             tag: 'tbody',
5392             cn : [
5393                 {
5394                     tag: 'tr',
5395                     cn : [
5396                         {
5397                             tag : 'td',
5398                             colspan :  this.cm.getColumnCount()
5399                         }
5400                     ]
5401                 }
5402             ]
5403         };
5404         
5405         return body;
5406     },
5407     
5408     renderFooter : function()
5409     {
5410         var footer = {
5411             tag: 'tfoot',
5412             cn : [
5413                 {
5414                     tag: 'tr',
5415                     cn : [
5416                         {
5417                             tag : 'td',
5418                             colspan :  this.cm.getColumnCount()
5419                         }
5420                     ]
5421                 }
5422             ]
5423         };
5424         
5425         return footer;
5426     },
5427     
5428     
5429     
5430     onLoad : function()
5431     {
5432         Roo.log('ds onload');
5433         this.clear();
5434         
5435         var _this = this;
5436         var cm = this.cm;
5437         var ds = this.store;
5438         
5439         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5440             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
5441             
5442             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
5443                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
5444             }
5445             
5446             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
5447                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
5448             }
5449         });
5450         
5451         var tbody =  this.mainBody;
5452               
5453         if(ds.getCount() > 0){
5454             ds.data.each(function(d,rowIndex){
5455                 var row =  this.renderRow(cm, ds, rowIndex);
5456                 
5457                 tbody.createChild(row);
5458                 
5459                 var _this = this;
5460                 
5461                 if(row.cellObjects.length){
5462                     Roo.each(row.cellObjects, function(r){
5463                         _this.renderCellObject(r);
5464                     })
5465                 }
5466                 
5467             }, this);
5468         }
5469         
5470         Roo.each(this.el.select('tbody td', true).elements, function(e){
5471             e.on('mouseover', _this.onMouseover, _this);
5472         });
5473         
5474         Roo.each(this.el.select('tbody td', true).elements, function(e){
5475             e.on('mouseout', _this.onMouseout, _this);
5476         });
5477
5478         //if(this.loadMask){
5479         //    this.maskEl.hide();
5480         //}
5481     },
5482     
5483     
5484     onUpdate : function(ds,record)
5485     {
5486         this.refreshRow(record);
5487     },
5488     onRemove : function(ds, record, index, isUpdate){
5489         if(isUpdate !== true){
5490             this.fireEvent("beforerowremoved", this, index, record);
5491         }
5492         var bt = this.mainBody.dom;
5493         if(bt.rows[index]){
5494             bt.removeChild(bt.rows[index]);
5495         }
5496         
5497         if(isUpdate !== true){
5498             //this.stripeRows(index);
5499             //this.syncRowHeights(index, index);
5500             //this.layout();
5501             this.fireEvent("rowremoved", this, index, record);
5502         }
5503     },
5504     
5505     onAdd : function(ds, records, rowIndex)
5506     {
5507         //Roo.log('on Add called');
5508         // - note this does not handle multiple adding very well..
5509         var bt = this.mainBody.dom;
5510         for (var i =0 ; i < records.length;i++) {
5511             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
5512             //Roo.log(records[i]);
5513             //Roo.log(this.store.getAt(rowIndex+i));
5514             this.insertRow(this.store, rowIndex + i, false);
5515             return;
5516         }
5517         
5518     },
5519     
5520     
5521     refreshRow : function(record){
5522         var ds = this.store, index;
5523         if(typeof record == 'number'){
5524             index = record;
5525             record = ds.getAt(index);
5526         }else{
5527             index = ds.indexOf(record);
5528         }
5529         this.insertRow(ds, index, true);
5530         this.onRemove(ds, record, index+1, true);
5531         //this.syncRowHeights(index, index);
5532         //this.layout();
5533         this.fireEvent("rowupdated", this, index, record);
5534     },
5535     
5536     insertRow : function(dm, rowIndex, isUpdate){
5537         
5538         if(!isUpdate){
5539             this.fireEvent("beforerowsinserted", this, rowIndex);
5540         }
5541             //var s = this.getScrollState();
5542         var row = this.renderRow(this.cm, this.store, rowIndex);
5543         // insert before rowIndex..
5544         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
5545         
5546         var _this = this;
5547                 
5548         if(row.cellObjects.length){
5549             Roo.each(row.cellObjects, function(r){
5550                 _this.renderCellObject(r);
5551             })
5552         }
5553             
5554         if(!isUpdate){
5555             this.fireEvent("rowsinserted", this, rowIndex);
5556             //this.syncRowHeights(firstRow, lastRow);
5557             //this.stripeRows(firstRow);
5558             //this.layout();
5559         }
5560         
5561     },
5562     
5563     
5564     getRowDom : function(rowIndex)
5565     {
5566         // not sure if I need to check this.. but let's do it anyway..
5567         return (this.mainBody.dom.rows && (rowIndex-1) < this.mainBody.dom.rows.length ) ?
5568                 this.mainBody.dom.rows[rowIndex] : false
5569     },
5570     // returns the object tree for a tr..
5571   
5572     
5573     renderRow : function(cm, ds, rowIndex) {
5574         
5575         var d = ds.getAt(rowIndex);
5576         
5577         var row = {
5578             tag : 'tr',
5579             cn : []
5580         };
5581             
5582         var cellObjects = [];
5583         
5584         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5585             var config = cm.config[i];
5586             
5587             var renderer = cm.getRenderer(i);
5588             var value = '';
5589             var id = false;
5590             
5591             if(typeof(renderer) !== 'undefined'){
5592                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
5593             }
5594             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
5595             // and are rendered into the cells after the row is rendered - using the id for the element.
5596             
5597             if(typeof(value) === 'object'){
5598                 id = Roo.id();
5599                 cellObjects.push({
5600                     container : id,
5601                     cfg : value 
5602                 })
5603             }
5604             
5605             var rowcfg = {
5606                 record: d,
5607                 rowIndex : rowIndex,
5608                 colIndex : i,
5609                 rowClass : ''
5610             }
5611
5612             this.fireEvent('rowclass', this, rowcfg);
5613             
5614             var td = {
5615                 tag: 'td',
5616                 cls : rowcfg.rowClass,
5617                 style: '',
5618                 html: (typeof(value) === 'object') ? '' : value
5619             };
5620             
5621             if (id) {
5622                 td.id = id;
5623             }
5624             
5625             if(typeof(config.hidden) != 'undefined' && config.hidden){
5626                 td.style += ' display:none;';
5627             }
5628             
5629             if(typeof(config.align) != 'undefined' && config.align.length){
5630                 td.style += ' text-align:' + config.align + ';';
5631             }
5632             
5633             if(typeof(config.width) != 'undefined'){
5634                 td.style += ' width:' +  config.width + 'px;';
5635             }
5636             
5637             if(typeof(config.cursor) != 'undefined'){
5638                 td.style += ' cursor:' +  config.cursor + ';';
5639             }
5640              
5641             row.cn.push(td);
5642            
5643         }
5644         
5645         row.cellObjects = cellObjects;
5646         
5647         return row;
5648           
5649     },
5650     
5651     
5652     
5653     onBeforeLoad : function()
5654     {
5655         //Roo.log('ds onBeforeLoad');
5656         
5657         //this.clear();
5658         
5659         //if(this.loadMask){
5660         //    this.maskEl.show();
5661         //}
5662     },
5663      /**
5664      * Remove all rows
5665      */
5666     clear : function()
5667     {
5668         this.el.select('tbody', true).first().dom.innerHTML = '';
5669     },
5670     /**
5671      * Show or hide a row.
5672      * @param {Number} rowIndex to show or hide
5673      * @param {Boolean} state hide
5674      */
5675     setRowVisibility : function(rowIndex, state)
5676     {
5677         var bt = this.mainBody.dom;
5678         if(typeof(bt.rows[rowIndex]) == 'undefined'){
5679             return;
5680         }
5681         bt.rows[rowIndex].style.display = state ? '' : 'none';
5682     },
5683     
5684     
5685     getSelectionModel : function(){
5686         if(!this.selModel){
5687             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
5688         }
5689         return this.selModel;
5690     },
5691     /*
5692      * Render the Roo.bootstrap object from renderder
5693      */
5694     renderCellObject : function(r)
5695     {
5696         var _this = this;
5697         
5698         var t = r.cfg.render(r.container);
5699         
5700         if(r.cfg.cn){
5701             Roo.each(r.cfg.cn, function(c){
5702                 var child = {
5703                     container: t.getChildContainer(),
5704                     cfg: c
5705                 }
5706                 _this.renderCellObject(child);
5707             })
5708         }
5709     }
5710    
5711 });
5712
5713  
5714
5715  /*
5716  * - LGPL
5717  *
5718  * table cell
5719  * 
5720  */
5721
5722 /**
5723  * @class Roo.bootstrap.TableCell
5724  * @extends Roo.bootstrap.Component
5725  * Bootstrap TableCell class
5726  * @cfg {String} html cell contain text
5727  * @cfg {String} cls cell class
5728  * @cfg {String} tag cell tag (td|th) default td
5729  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
5730  * @cfg {String} align Aligns the content in a cell
5731  * @cfg {String} axis Categorizes cells
5732  * @cfg {String} bgcolor Specifies the background color of a cell
5733  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5734  * @cfg {Number} colspan Specifies the number of columns a cell should span
5735  * @cfg {String} headers Specifies one or more header cells a cell is related to
5736  * @cfg {Number} height Sets the height of a cell
5737  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
5738  * @cfg {Number} rowspan Sets the number of rows a cell should span
5739  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
5740  * @cfg {String} valign Vertical aligns the content in a cell
5741  * @cfg {Number} width Specifies the width of a cell
5742  * 
5743  * @constructor
5744  * Create a new TableCell
5745  * @param {Object} config The config object
5746  */
5747
5748 Roo.bootstrap.TableCell = function(config){
5749     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
5750 };
5751
5752 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
5753     
5754     html: false,
5755     cls: false,
5756     tag: false,
5757     abbr: false,
5758     align: false,
5759     axis: false,
5760     bgcolor: false,
5761     charoff: false,
5762     colspan: false,
5763     headers: false,
5764     height: false,
5765     nowrap: false,
5766     rowspan: false,
5767     scope: false,
5768     valign: false,
5769     width: false,
5770     
5771     
5772     getAutoCreate : function(){
5773         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
5774         
5775         cfg = {
5776             tag: 'td'
5777         }
5778         
5779         if(this.tag){
5780             cfg.tag = this.tag;
5781         }
5782         
5783         if (this.html) {
5784             cfg.html=this.html
5785         }
5786         if (this.cls) {
5787             cfg.cls=this.cls
5788         }
5789         if (this.abbr) {
5790             cfg.abbr=this.abbr
5791         }
5792         if (this.align) {
5793             cfg.align=this.align
5794         }
5795         if (this.axis) {
5796             cfg.axis=this.axis
5797         }
5798         if (this.bgcolor) {
5799             cfg.bgcolor=this.bgcolor
5800         }
5801         if (this.charoff) {
5802             cfg.charoff=this.charoff
5803         }
5804         if (this.colspan) {
5805             cfg.colspan=this.colspan
5806         }
5807         if (this.headers) {
5808             cfg.headers=this.headers
5809         }
5810         if (this.height) {
5811             cfg.height=this.height
5812         }
5813         if (this.nowrap) {
5814             cfg.nowrap=this.nowrap
5815         }
5816         if (this.rowspan) {
5817             cfg.rowspan=this.rowspan
5818         }
5819         if (this.scope) {
5820             cfg.scope=this.scope
5821         }
5822         if (this.valign) {
5823             cfg.valign=this.valign
5824         }
5825         if (this.width) {
5826             cfg.width=this.width
5827         }
5828         
5829         
5830         return cfg;
5831     }
5832    
5833 });
5834
5835  
5836
5837  /*
5838  * - LGPL
5839  *
5840  * table row
5841  * 
5842  */
5843
5844 /**
5845  * @class Roo.bootstrap.TableRow
5846  * @extends Roo.bootstrap.Component
5847  * Bootstrap TableRow class
5848  * @cfg {String} cls row class
5849  * @cfg {String} align Aligns the content in a table row
5850  * @cfg {String} bgcolor Specifies a background color for a table row
5851  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5852  * @cfg {String} valign Vertical aligns the content in a table row
5853  * 
5854  * @constructor
5855  * Create a new TableRow
5856  * @param {Object} config The config object
5857  */
5858
5859 Roo.bootstrap.TableRow = function(config){
5860     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
5861 };
5862
5863 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
5864     
5865     cls: false,
5866     align: false,
5867     bgcolor: false,
5868     charoff: false,
5869     valign: false,
5870     
5871     getAutoCreate : function(){
5872         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
5873         
5874         cfg = {
5875             tag: 'tr'
5876         }
5877             
5878         if(this.cls){
5879             cfg.cls = this.cls;
5880         }
5881         if(this.align){
5882             cfg.align = this.align;
5883         }
5884         if(this.bgcolor){
5885             cfg.bgcolor = this.bgcolor;
5886         }
5887         if(this.charoff){
5888             cfg.charoff = this.charoff;
5889         }
5890         if(this.valign){
5891             cfg.valign = this.valign;
5892         }
5893         
5894         return cfg;
5895     }
5896    
5897 });
5898
5899  
5900
5901  /*
5902  * - LGPL
5903  *
5904  * table body
5905  * 
5906  */
5907
5908 /**
5909  * @class Roo.bootstrap.TableBody
5910  * @extends Roo.bootstrap.Component
5911  * Bootstrap TableBody class
5912  * @cfg {String} cls element class
5913  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
5914  * @cfg {String} align Aligns the content inside the element
5915  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
5916  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
5917  * 
5918  * @constructor
5919  * Create a new TableBody
5920  * @param {Object} config The config object
5921  */
5922
5923 Roo.bootstrap.TableBody = function(config){
5924     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
5925 };
5926
5927 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
5928     
5929     cls: false,
5930     tag: false,
5931     align: false,
5932     charoff: false,
5933     valign: false,
5934     
5935     getAutoCreate : function(){
5936         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
5937         
5938         cfg = {
5939             tag: 'tbody'
5940         }
5941             
5942         if (this.cls) {
5943             cfg.cls=this.cls
5944         }
5945         if(this.tag){
5946             cfg.tag = this.tag;
5947         }
5948         
5949         if(this.align){
5950             cfg.align = this.align;
5951         }
5952         if(this.charoff){
5953             cfg.charoff = this.charoff;
5954         }
5955         if(this.valign){
5956             cfg.valign = this.valign;
5957         }
5958         
5959         return cfg;
5960     }
5961     
5962     
5963 //    initEvents : function()
5964 //    {
5965 //        
5966 //        if(!this.store){
5967 //            return;
5968 //        }
5969 //        
5970 //        this.store = Roo.factory(this.store, Roo.data);
5971 //        this.store.on('load', this.onLoad, this);
5972 //        
5973 //        this.store.load();
5974 //        
5975 //    },
5976 //    
5977 //    onLoad: function () 
5978 //    {   
5979 //        this.fireEvent('load', this);
5980 //    }
5981 //    
5982 //   
5983 });
5984
5985  
5986
5987  /*
5988  * Based on:
5989  * Ext JS Library 1.1.1
5990  * Copyright(c) 2006-2007, Ext JS, LLC.
5991  *
5992  * Originally Released Under LGPL - original licence link has changed is not relivant.
5993  *
5994  * Fork - LGPL
5995  * <script type="text/javascript">
5996  */
5997
5998 // as we use this in bootstrap.
5999 Roo.namespace('Roo.form');
6000  /**
6001  * @class Roo.form.Action
6002  * Internal Class used to handle form actions
6003  * @constructor
6004  * @param {Roo.form.BasicForm} el The form element or its id
6005  * @param {Object} config Configuration options
6006  */
6007
6008  
6009  
6010 // define the action interface
6011 Roo.form.Action = function(form, options){
6012     this.form = form;
6013     this.options = options || {};
6014 };
6015 /**
6016  * Client Validation Failed
6017  * @const 
6018  */
6019 Roo.form.Action.CLIENT_INVALID = 'client';
6020 /**
6021  * Server Validation Failed
6022  * @const 
6023  */
6024 Roo.form.Action.SERVER_INVALID = 'server';
6025  /**
6026  * Connect to Server Failed
6027  * @const 
6028  */
6029 Roo.form.Action.CONNECT_FAILURE = 'connect';
6030 /**
6031  * Reading Data from Server Failed
6032  * @const 
6033  */
6034 Roo.form.Action.LOAD_FAILURE = 'load';
6035
6036 Roo.form.Action.prototype = {
6037     type : 'default',
6038     failureType : undefined,
6039     response : undefined,
6040     result : undefined,
6041
6042     // interface method
6043     run : function(options){
6044
6045     },
6046
6047     // interface method
6048     success : function(response){
6049
6050     },
6051
6052     // interface method
6053     handleResponse : function(response){
6054
6055     },
6056
6057     // default connection failure
6058     failure : function(response){
6059         
6060         this.response = response;
6061         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6062         this.form.afterAction(this, false);
6063     },
6064
6065     processResponse : function(response){
6066         this.response = response;
6067         if(!response.responseText){
6068             return true;
6069         }
6070         this.result = this.handleResponse(response);
6071         return this.result;
6072     },
6073
6074     // utility functions used internally
6075     getUrl : function(appendParams){
6076         var url = this.options.url || this.form.url || this.form.el.dom.action;
6077         if(appendParams){
6078             var p = this.getParams();
6079             if(p){
6080                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
6081             }
6082         }
6083         return url;
6084     },
6085
6086     getMethod : function(){
6087         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
6088     },
6089
6090     getParams : function(){
6091         var bp = this.form.baseParams;
6092         var p = this.options.params;
6093         if(p){
6094             if(typeof p == "object"){
6095                 p = Roo.urlEncode(Roo.applyIf(p, bp));
6096             }else if(typeof p == 'string' && bp){
6097                 p += '&' + Roo.urlEncode(bp);
6098             }
6099         }else if(bp){
6100             p = Roo.urlEncode(bp);
6101         }
6102         return p;
6103     },
6104
6105     createCallback : function(){
6106         return {
6107             success: this.success,
6108             failure: this.failure,
6109             scope: this,
6110             timeout: (this.form.timeout*1000),
6111             upload: this.form.fileUpload ? this.success : undefined
6112         };
6113     }
6114 };
6115
6116 Roo.form.Action.Submit = function(form, options){
6117     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
6118 };
6119
6120 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
6121     type : 'submit',
6122
6123     haveProgress : false,
6124     uploadComplete : false,
6125     
6126     // uploadProgress indicator.
6127     uploadProgress : function()
6128     {
6129         if (!this.form.progressUrl) {
6130             return;
6131         }
6132         
6133         if (!this.haveProgress) {
6134             Roo.MessageBox.progress("Uploading", "Uploading");
6135         }
6136         if (this.uploadComplete) {
6137            Roo.MessageBox.hide();
6138            return;
6139         }
6140         
6141         this.haveProgress = true;
6142    
6143         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6144         
6145         var c = new Roo.data.Connection();
6146         c.request({
6147             url : this.form.progressUrl,
6148             params: {
6149                 id : uid
6150             },
6151             method: 'GET',
6152             success : function(req){
6153                //console.log(data);
6154                 var rdata = false;
6155                 var edata;
6156                 try  {
6157                    rdata = Roo.decode(req.responseText)
6158                 } catch (e) {
6159                     Roo.log("Invalid data from server..");
6160                     Roo.log(edata);
6161                     return;
6162                 }
6163                 if (!rdata || !rdata.success) {
6164                     Roo.log(rdata);
6165                     Roo.MessageBox.alert(Roo.encode(rdata));
6166                     return;
6167                 }
6168                 var data = rdata.data;
6169                 
6170                 if (this.uploadComplete) {
6171                    Roo.MessageBox.hide();
6172                    return;
6173                 }
6174                    
6175                 if (data){
6176                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6177                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6178                     );
6179                 }
6180                 this.uploadProgress.defer(2000,this);
6181             },
6182        
6183             failure: function(data) {
6184                 Roo.log('progress url failed ');
6185                 Roo.log(data);
6186             },
6187             scope : this
6188         });
6189            
6190     },
6191     
6192     
6193     run : function()
6194     {
6195         // run get Values on the form, so it syncs any secondary forms.
6196         this.form.getValues();
6197         
6198         var o = this.options;
6199         var method = this.getMethod();
6200         var isPost = method == 'POST';
6201         if(o.clientValidation === false || this.form.isValid()){
6202             
6203             if (this.form.progressUrl) {
6204                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6205                     (new Date() * 1) + '' + Math.random());
6206                     
6207             } 
6208             
6209             
6210             Roo.Ajax.request(Roo.apply(this.createCallback(), {
6211                 form:this.form.el.dom,
6212                 url:this.getUrl(!isPost),
6213                 method: method,
6214                 params:isPost ? this.getParams() : null,
6215                 isUpload: this.form.fileUpload
6216             }));
6217             
6218             this.uploadProgress();
6219
6220         }else if (o.clientValidation !== false){ // client validation failed
6221             this.failureType = Roo.form.Action.CLIENT_INVALID;
6222             this.form.afterAction(this, false);
6223         }
6224     },
6225
6226     success : function(response)
6227     {
6228         this.uploadComplete= true;
6229         if (this.haveProgress) {
6230             Roo.MessageBox.hide();
6231         }
6232         
6233         
6234         var result = this.processResponse(response);
6235         if(result === true || result.success){
6236             this.form.afterAction(this, true);
6237             return;
6238         }
6239         if(result.errors){
6240             this.form.markInvalid(result.errors);
6241             this.failureType = Roo.form.Action.SERVER_INVALID;
6242         }
6243         this.form.afterAction(this, false);
6244     },
6245     failure : function(response)
6246     {
6247         this.uploadComplete= true;
6248         if (this.haveProgress) {
6249             Roo.MessageBox.hide();
6250         }
6251         
6252         this.response = response;
6253         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6254         this.form.afterAction(this, false);
6255     },
6256     
6257     handleResponse : function(response){
6258         if(this.form.errorReader){
6259             var rs = this.form.errorReader.read(response);
6260             var errors = [];
6261             if(rs.records){
6262                 for(var i = 0, len = rs.records.length; i < len; i++) {
6263                     var r = rs.records[i];
6264                     errors[i] = r.data;
6265                 }
6266             }
6267             if(errors.length < 1){
6268                 errors = null;
6269             }
6270             return {
6271                 success : rs.success,
6272                 errors : errors
6273             };
6274         }
6275         var ret = false;
6276         try {
6277             ret = Roo.decode(response.responseText);
6278         } catch (e) {
6279             ret = {
6280                 success: false,
6281                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6282                 errors : []
6283             };
6284         }
6285         return ret;
6286         
6287     }
6288 });
6289
6290
6291 Roo.form.Action.Load = function(form, options){
6292     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6293     this.reader = this.form.reader;
6294 };
6295
6296 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6297     type : 'load',
6298
6299     run : function(){
6300         
6301         Roo.Ajax.request(Roo.apply(
6302                 this.createCallback(), {
6303                     method:this.getMethod(),
6304                     url:this.getUrl(false),
6305                     params:this.getParams()
6306         }));
6307     },
6308
6309     success : function(response){
6310         
6311         var result = this.processResponse(response);
6312         if(result === true || !result.success || !result.data){
6313             this.failureType = Roo.form.Action.LOAD_FAILURE;
6314             this.form.afterAction(this, false);
6315             return;
6316         }
6317         this.form.clearInvalid();
6318         this.form.setValues(result.data);
6319         this.form.afterAction(this, true);
6320     },
6321
6322     handleResponse : function(response){
6323         if(this.form.reader){
6324             var rs = this.form.reader.read(response);
6325             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
6326             return {
6327                 success : rs.success,
6328                 data : data
6329             };
6330         }
6331         return Roo.decode(response.responseText);
6332     }
6333 });
6334
6335 Roo.form.Action.ACTION_TYPES = {
6336     'load' : Roo.form.Action.Load,
6337     'submit' : Roo.form.Action.Submit
6338 };/*
6339  * - LGPL
6340  *
6341  * form
6342  * 
6343  */
6344
6345 /**
6346  * @class Roo.bootstrap.Form
6347  * @extends Roo.bootstrap.Component
6348  * Bootstrap Form class
6349  * @cfg {String} method  GET | POST (default POST)
6350  * @cfg {String} labelAlign top | left (default top)
6351  * @cfg {String} align left  | right - for navbars
6352  * @cfg {Boolean} loadMask load mask when submit (default true)
6353
6354  * 
6355  * @constructor
6356  * Create a new Form
6357  * @param {Object} config The config object
6358  */
6359
6360
6361 Roo.bootstrap.Form = function(config){
6362     Roo.bootstrap.Form.superclass.constructor.call(this, config);
6363     this.addEvents({
6364         /**
6365          * @event clientvalidation
6366          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
6367          * @param {Form} this
6368          * @param {Boolean} valid true if the form has passed client-side validation
6369          */
6370         clientvalidation: true,
6371         /**
6372          * @event beforeaction
6373          * Fires before any action is performed. Return false to cancel the action.
6374          * @param {Form} this
6375          * @param {Action} action The action to be performed
6376          */
6377         beforeaction: true,
6378         /**
6379          * @event actionfailed
6380          * Fires when an action fails.
6381          * @param {Form} this
6382          * @param {Action} action The action that failed
6383          */
6384         actionfailed : true,
6385         /**
6386          * @event actioncomplete
6387          * Fires when an action is completed.
6388          * @param {Form} this
6389          * @param {Action} action The action that completed
6390          */
6391         actioncomplete : true
6392     });
6393     
6394 };
6395
6396 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
6397       
6398      /**
6399      * @cfg {String} method
6400      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
6401      */
6402     method : 'POST',
6403     /**
6404      * @cfg {String} url
6405      * The URL to use for form actions if one isn't supplied in the action options.
6406      */
6407     /**
6408      * @cfg {Boolean} fileUpload
6409      * Set to true if this form is a file upload.
6410      */
6411      
6412     /**
6413      * @cfg {Object} baseParams
6414      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
6415      */
6416       
6417     /**
6418      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
6419      */
6420     timeout: 30,
6421     /**
6422      * @cfg {Sting} align (left|right) for navbar forms
6423      */
6424     align : 'left',
6425
6426     // private
6427     activeAction : null,
6428  
6429     /**
6430      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6431      * element by passing it or its id or mask the form itself by passing in true.
6432      * @type Mixed
6433      */
6434     waitMsgTarget : false,
6435     
6436     loadMask : true,
6437     
6438     getAutoCreate : function(){
6439         
6440         var cfg = {
6441             tag: 'form',
6442             method : this.method || 'POST',
6443             id : this.id || Roo.id(),
6444             cls : ''
6445         }
6446         if (this.parent().xtype.match(/^Nav/)) {
6447             cfg.cls = 'navbar-form navbar-' + this.align;
6448             
6449         }
6450         
6451         if (this.labelAlign == 'left' ) {
6452             cfg.cls += ' form-horizontal';
6453         }
6454         
6455         
6456         return cfg;
6457     },
6458     initEvents : function()
6459     {
6460         this.el.on('submit', this.onSubmit, this);
6461         // this was added as random key presses on the form where triggering form submit.
6462         this.el.on('keypress', function(e) {
6463             if (e.getCharCode() != 13) {
6464                 return true;
6465             }
6466             // we might need to allow it for textareas.. and some other items.
6467             // check e.getTarget().
6468             
6469             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
6470                 return true;
6471             }
6472         
6473             Roo.log("keypress blocked");
6474             
6475             e.preventDefault();
6476             return false;
6477         });
6478         
6479     },
6480     // private
6481     onSubmit : function(e){
6482         e.stopEvent();
6483     },
6484     
6485      /**
6486      * Returns true if client-side validation on the form is successful.
6487      * @return Boolean
6488      */
6489     isValid : function(){
6490         var items = this.getItems();
6491         var valid = true;
6492         items.each(function(f){
6493            if(!f.validate()){
6494                valid = false;
6495                
6496            }
6497         });
6498         return valid;
6499     },
6500     /**
6501      * Returns true if any fields in this form have changed since their original load.
6502      * @return Boolean
6503      */
6504     isDirty : function(){
6505         var dirty = false;
6506         var items = this.getItems();
6507         items.each(function(f){
6508            if(f.isDirty()){
6509                dirty = true;
6510                return false;
6511            }
6512            return true;
6513         });
6514         return dirty;
6515     },
6516      /**
6517      * Performs a predefined action (submit or load) or custom actions you define on this form.
6518      * @param {String} actionName The name of the action type
6519      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
6520      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
6521      * accept other config options):
6522      * <pre>
6523 Property          Type             Description
6524 ----------------  ---------------  ----------------------------------------------------------------------------------
6525 url               String           The url for the action (defaults to the form's url)
6526 method            String           The form method to use (defaults to the form's method, or POST if not defined)
6527 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
6528 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
6529                                    validate the form on the client (defaults to false)
6530      * </pre>
6531      * @return {BasicForm} this
6532      */
6533     doAction : function(action, options){
6534         if(typeof action == 'string'){
6535             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
6536         }
6537         if(this.fireEvent('beforeaction', this, action) !== false){
6538             this.beforeAction(action);
6539             action.run.defer(100, action);
6540         }
6541         return this;
6542     },
6543     
6544     // private
6545     beforeAction : function(action){
6546         var o = action.options;
6547         
6548         if(this.loadMask){
6549             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6550         }
6551         // not really supported yet.. ??
6552         
6553         //if(this.waitMsgTarget === true){
6554         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6555         //}else if(this.waitMsgTarget){
6556         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
6557         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
6558         //}else {
6559         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
6560        // }
6561          
6562     },
6563
6564     // private
6565     afterAction : function(action, success){
6566         this.activeAction = null;
6567         var o = action.options;
6568         
6569         //if(this.waitMsgTarget === true){
6570             this.el.unmask();
6571         //}else if(this.waitMsgTarget){
6572         //    this.waitMsgTarget.unmask();
6573         //}else{
6574         //    Roo.MessageBox.updateProgress(1);
6575         //    Roo.MessageBox.hide();
6576        // }
6577         // 
6578         if(success){
6579             if(o.reset){
6580                 this.reset();
6581             }
6582             Roo.callback(o.success, o.scope, [this, action]);
6583             this.fireEvent('actioncomplete', this, action);
6584             
6585         }else{
6586             
6587             // failure condition..
6588             // we have a scenario where updates need confirming.
6589             // eg. if a locking scenario exists..
6590             // we look for { errors : { needs_confirm : true }} in the response.
6591             if (
6592                 (typeof(action.result) != 'undefined')  &&
6593                 (typeof(action.result.errors) != 'undefined')  &&
6594                 (typeof(action.result.errors.needs_confirm) != 'undefined')
6595            ){
6596                 var _t = this;
6597                 Roo.log("not supported yet");
6598                  /*
6599                 
6600                 Roo.MessageBox.confirm(
6601                     "Change requires confirmation",
6602                     action.result.errorMsg,
6603                     function(r) {
6604                         if (r != 'yes') {
6605                             return;
6606                         }
6607                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
6608                     }
6609                     
6610                 );
6611                 */
6612                 
6613                 
6614                 return;
6615             }
6616             
6617             Roo.callback(o.failure, o.scope, [this, action]);
6618             // show an error message if no failed handler is set..
6619             if (!this.hasListener('actionfailed')) {
6620                 Roo.log("need to add dialog support");
6621                 /*
6622                 Roo.MessageBox.alert("Error",
6623                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
6624                         action.result.errorMsg :
6625                         "Saving Failed, please check your entries or try again"
6626                 );
6627                 */
6628             }
6629             
6630             this.fireEvent('actionfailed', this, action);
6631         }
6632         
6633     },
6634     /**
6635      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
6636      * @param {String} id The value to search for
6637      * @return Field
6638      */
6639     findField : function(id){
6640         var items = this.getItems();
6641         var field = items.get(id);
6642         if(!field){
6643              items.each(function(f){
6644                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
6645                     field = f;
6646                     return false;
6647                 }
6648                 return true;
6649             });
6650         }
6651         return field || null;
6652     },
6653      /**
6654      * Mark fields in this form invalid in bulk.
6655      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
6656      * @return {BasicForm} this
6657      */
6658     markInvalid : function(errors){
6659         if(errors instanceof Array){
6660             for(var i = 0, len = errors.length; i < len; i++){
6661                 var fieldError = errors[i];
6662                 var f = this.findField(fieldError.id);
6663                 if(f){
6664                     f.markInvalid(fieldError.msg);
6665                 }
6666             }
6667         }else{
6668             var field, id;
6669             for(id in errors){
6670                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
6671                     field.markInvalid(errors[id]);
6672                 }
6673             }
6674         }
6675         //Roo.each(this.childForms || [], function (f) {
6676         //    f.markInvalid(errors);
6677         //});
6678         
6679         return this;
6680     },
6681
6682     /**
6683      * Set values for fields in this form in bulk.
6684      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
6685      * @return {BasicForm} this
6686      */
6687     setValues : function(values){
6688         if(values instanceof Array){ // array of objects
6689             for(var i = 0, len = values.length; i < len; i++){
6690                 var v = values[i];
6691                 var f = this.findField(v.id);
6692                 if(f){
6693                     f.setValue(v.value);
6694                     if(this.trackResetOnLoad){
6695                         f.originalValue = f.getValue();
6696                     }
6697                 }
6698             }
6699         }else{ // object hash
6700             var field, id;
6701             for(id in values){
6702                 if(typeof values[id] != 'function' && (field = this.findField(id))){
6703                     
6704                     if (field.setFromData && 
6705                         field.valueField && 
6706                         field.displayField &&
6707                         // combos' with local stores can 
6708                         // be queried via setValue()
6709                         // to set their value..
6710                         (field.store && !field.store.isLocal)
6711                         ) {
6712                         // it's a combo
6713                         var sd = { };
6714                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
6715                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
6716                         field.setFromData(sd);
6717                         
6718                     } else {
6719                         field.setValue(values[id]);
6720                     }
6721                     
6722                     
6723                     if(this.trackResetOnLoad){
6724                         field.originalValue = field.getValue();
6725                     }
6726                 }
6727             }
6728         }
6729          
6730         //Roo.each(this.childForms || [], function (f) {
6731         //    f.setValues(values);
6732         //});
6733                 
6734         return this;
6735     },
6736
6737     /**
6738      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
6739      * they are returned as an array.
6740      * @param {Boolean} asString
6741      * @return {Object}
6742      */
6743     getValues : function(asString){
6744         //if (this.childForms) {
6745             // copy values from the child forms
6746         //    Roo.each(this.childForms, function (f) {
6747         //        this.setValues(f.getValues());
6748         //    }, this);
6749         //}
6750         
6751         
6752         
6753         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
6754         if(asString === true){
6755             return fs;
6756         }
6757         return Roo.urlDecode(fs);
6758     },
6759     
6760     /**
6761      * Returns the fields in this form as an object with key/value pairs. 
6762      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
6763      * @return {Object}
6764      */
6765     getFieldValues : function(with_hidden)
6766     {
6767         var items = this.getItems();
6768         var ret = {};
6769         items.each(function(f){
6770             if (!f.getName()) {
6771                 return;
6772             }
6773             var v = f.getValue();
6774             if (f.inputType =='radio') {
6775                 if (typeof(ret[f.getName()]) == 'undefined') {
6776                     ret[f.getName()] = ''; // empty..
6777                 }
6778                 
6779                 if (!f.el.dom.checked) {
6780                     return;
6781                     
6782                 }
6783                 v = f.el.dom.value;
6784                 
6785             }
6786             
6787             // not sure if this supported any more..
6788             if ((typeof(v) == 'object') && f.getRawValue) {
6789                 v = f.getRawValue() ; // dates..
6790             }
6791             // combo boxes where name != hiddenName...
6792             if (f.name != f.getName()) {
6793                 ret[f.name] = f.getRawValue();
6794             }
6795             ret[f.getName()] = v;
6796         });
6797         
6798         return ret;
6799     },
6800
6801     /**
6802      * Clears all invalid messages in this form.
6803      * @return {BasicForm} this
6804      */
6805     clearInvalid : function(){
6806         var items = this.getItems();
6807         
6808         items.each(function(f){
6809            f.clearInvalid();
6810         });
6811         
6812         
6813         
6814         return this;
6815     },
6816
6817     /**
6818      * Resets this form.
6819      * @return {BasicForm} this
6820      */
6821     reset : function(){
6822         var items = this.getItems();
6823         items.each(function(f){
6824             f.reset();
6825         });
6826         
6827         Roo.each(this.childForms || [], function (f) {
6828             f.reset();
6829         });
6830        
6831         
6832         return this;
6833     },
6834     getItems : function()
6835     {
6836         var r=new Roo.util.MixedCollection(false, function(o){
6837             return o.id || (o.id = Roo.id());
6838         });
6839         var iter = function(el) {
6840             if (el.inputEl) {
6841                 r.add(el);
6842             }
6843             if (!el.items) {
6844                 return;
6845             }
6846             Roo.each(el.items,function(e) {
6847                 iter(e);
6848             });
6849             
6850             
6851         };
6852         iter(this);
6853         return r;
6854         
6855         
6856         
6857         
6858     }
6859     
6860 });
6861
6862  
6863 /*
6864  * Based on:
6865  * Ext JS Library 1.1.1
6866  * Copyright(c) 2006-2007, Ext JS, LLC.
6867  *
6868  * Originally Released Under LGPL - original licence link has changed is not relivant.
6869  *
6870  * Fork - LGPL
6871  * <script type="text/javascript">
6872  */
6873 /**
6874  * @class Roo.form.VTypes
6875  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
6876  * @singleton
6877  */
6878 Roo.form.VTypes = function(){
6879     // closure these in so they are only created once.
6880     var alpha = /^[a-zA-Z_]+$/;
6881     var alphanum = /^[a-zA-Z0-9_]+$/;
6882     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
6883     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
6884
6885     // All these messages and functions are configurable
6886     return {
6887         /**
6888          * The function used to validate email addresses
6889          * @param {String} value The email address
6890          */
6891         'email' : function(v){
6892             return email.test(v);
6893         },
6894         /**
6895          * The error text to display when the email validation function returns false
6896          * @type String
6897          */
6898         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
6899         /**
6900          * The keystroke filter mask to be applied on email input
6901          * @type RegExp
6902          */
6903         'emailMask' : /[a-z0-9_\.\-@]/i,
6904
6905         /**
6906          * The function used to validate URLs
6907          * @param {String} value The URL
6908          */
6909         'url' : function(v){
6910             return url.test(v);
6911         },
6912         /**
6913          * The error text to display when the url validation function returns false
6914          * @type String
6915          */
6916         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
6917         
6918         /**
6919          * The function used to validate alpha values
6920          * @param {String} value The value
6921          */
6922         'alpha' : function(v){
6923             return alpha.test(v);
6924         },
6925         /**
6926          * The error text to display when the alpha validation function returns false
6927          * @type String
6928          */
6929         'alphaText' : 'This field should only contain letters and _',
6930         /**
6931          * The keystroke filter mask to be applied on alpha input
6932          * @type RegExp
6933          */
6934         'alphaMask' : /[a-z_]/i,
6935
6936         /**
6937          * The function used to validate alphanumeric values
6938          * @param {String} value The value
6939          */
6940         'alphanum' : function(v){
6941             return alphanum.test(v);
6942         },
6943         /**
6944          * The error text to display when the alphanumeric validation function returns false
6945          * @type String
6946          */
6947         'alphanumText' : 'This field should only contain letters, numbers and _',
6948         /**
6949          * The keystroke filter mask to be applied on alphanumeric input
6950          * @type RegExp
6951          */
6952         'alphanumMask' : /[a-z0-9_]/i
6953     };
6954 }();/*
6955  * - LGPL
6956  *
6957  * Input
6958  * 
6959  */
6960
6961 /**
6962  * @class Roo.bootstrap.Input
6963  * @extends Roo.bootstrap.Component
6964  * Bootstrap Input class
6965  * @cfg {Boolean} disabled is it disabled
6966  * @cfg {String} fieldLabel - the label associated
6967  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
6968  * @cfg {String} name name of the input
6969  * @cfg {string} fieldLabel - the label associated
6970  * @cfg {string}  inputType - input / file submit ...
6971  * @cfg {string} placeholder - placeholder to put in text.
6972  * @cfg {string}  before - input group add on before
6973  * @cfg {string} after - input group add on after
6974  * @cfg {string} size - (lg|sm) or leave empty..
6975  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
6976  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
6977  * @cfg {Number} md colspan out of 12 for computer-sized screens
6978  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
6979  * @cfg {string} value default value of the input
6980  * @cfg {Number} labelWidth set the width of label (0-12)
6981  * @cfg {String} labelAlign (top|left)
6982  * @cfg {Boolean} readOnly Specifies that the field should be read-only
6983  * @cfg {String} align (left|center|right) Default left
6984  * 
6985  * 
6986  * @constructor
6987  * Create a new Input
6988  * @param {Object} config The config object
6989  */
6990
6991 Roo.bootstrap.Input = function(config){
6992     Roo.bootstrap.Input.superclass.constructor.call(this, config);
6993    
6994         this.addEvents({
6995             /**
6996              * @event focus
6997              * Fires when this field receives input focus.
6998              * @param {Roo.form.Field} this
6999              */
7000             focus : true,
7001             /**
7002              * @event blur
7003              * Fires when this field loses input focus.
7004              * @param {Roo.form.Field} this
7005              */
7006             blur : true,
7007             /**
7008              * @event specialkey
7009              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
7010              * {@link Roo.EventObject#getKey} to determine which key was pressed.
7011              * @param {Roo.form.Field} this
7012              * @param {Roo.EventObject} e The event object
7013              */
7014             specialkey : true,
7015             /**
7016              * @event change
7017              * Fires just before the field blurs if the field value has changed.
7018              * @param {Roo.form.Field} this
7019              * @param {Mixed} newValue The new value
7020              * @param {Mixed} oldValue The original value
7021              */
7022             change : true,
7023             /**
7024              * @event invalid
7025              * Fires after the field has been marked as invalid.
7026              * @param {Roo.form.Field} this
7027              * @param {String} msg The validation message
7028              */
7029             invalid : true,
7030             /**
7031              * @event valid
7032              * Fires after the field has been validated with no errors.
7033              * @param {Roo.form.Field} this
7034              */
7035             valid : true,
7036              /**
7037              * @event keyup
7038              * Fires after the key up
7039              * @param {Roo.form.Field} this
7040              * @param {Roo.EventObject}  e The event Object
7041              */
7042             keyup : true
7043         });
7044 };
7045
7046 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
7047      /**
7048      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
7049       automatic validation (defaults to "keyup").
7050      */
7051     validationEvent : "keyup",
7052      /**
7053      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
7054      */
7055     validateOnBlur : true,
7056     /**
7057      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
7058      */
7059     validationDelay : 250,
7060      /**
7061      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
7062      */
7063     focusClass : "x-form-focus",  // not needed???
7064     
7065        
7066     /**
7067      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
7068      */
7069     invalidClass : "has-error",
7070     
7071     /**
7072      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
7073      */
7074     selectOnFocus : false,
7075     
7076      /**
7077      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
7078      */
7079     maskRe : null,
7080        /**
7081      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
7082      */
7083     vtype : null,
7084     
7085       /**
7086      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
7087      */
7088     disableKeyFilter : false,
7089     
7090        /**
7091      * @cfg {Boolean} disabled True to disable the field (defaults to false).
7092      */
7093     disabled : false,
7094      /**
7095      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
7096      */
7097     allowBlank : true,
7098     /**
7099      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
7100      */
7101     blankText : "This field is required",
7102     
7103      /**
7104      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
7105      */
7106     minLength : 0,
7107     /**
7108      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
7109      */
7110     maxLength : Number.MAX_VALUE,
7111     /**
7112      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
7113      */
7114     minLengthText : "The minimum length for this field is {0}",
7115     /**
7116      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
7117      */
7118     maxLengthText : "The maximum length for this field is {0}",
7119   
7120     
7121     /**
7122      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
7123      * If available, this function will be called only after the basic validators all return true, and will be passed the
7124      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
7125      */
7126     validator : null,
7127     /**
7128      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
7129      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7130      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
7131      */
7132     regex : null,
7133     /**
7134      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7135      */
7136     regexText : "",
7137     
7138     
7139     
7140     fieldLabel : '',
7141     inputType : 'text',
7142     
7143     name : false,
7144     placeholder: false,
7145     before : false,
7146     after : false,
7147     size : false,
7148     // private
7149     hasFocus : false,
7150     preventMark: false,
7151     isFormField : true,
7152     value : '',
7153     labelWidth : 2,
7154     labelAlign : false,
7155     readOnly : false,
7156     align : false,
7157     formatedValue : false,
7158     
7159     parentLabelAlign : function()
7160     {
7161         var parent = this;
7162         while (parent.parent()) {
7163             parent = parent.parent();
7164             if (typeof(parent.labelAlign) !='undefined') {
7165                 return parent.labelAlign;
7166             }
7167         }
7168         return 'left';
7169         
7170     },
7171     
7172     getAutoCreate : function(){
7173         
7174         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7175         
7176         var id = Roo.id();
7177         
7178         var cfg = {};
7179         
7180         if(this.inputType != 'hidden'){
7181             cfg.cls = 'form-group' //input-group
7182         }
7183         
7184         var input =  {
7185             tag: 'input',
7186             id : id,
7187             type : this.inputType,
7188             value : this.value,
7189             cls : 'form-control',
7190             placeholder : this.placeholder || ''
7191             
7192         };
7193         
7194         if(this.align){
7195             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7196         }
7197         
7198         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7199             input.maxLength = this.maxLength;
7200         }
7201         
7202         if (this.disabled) {
7203             input.disabled=true;
7204         }
7205         
7206         if (this.readOnly) {
7207             input.readonly=true;
7208         }
7209         
7210         if (this.name) {
7211             input.name = this.name;
7212         }
7213         if (this.size) {
7214             input.cls += ' input-' + this.size;
7215         }
7216         var settings=this;
7217         ['xs','sm','md','lg'].map(function(size){
7218             if (settings[size]) {
7219                 cfg.cls += ' col-' + size + '-' + settings[size];
7220             }
7221         });
7222         
7223         var inputblock = input;
7224         
7225         if (this.before || this.after) {
7226             
7227             inputblock = {
7228                 cls : 'input-group',
7229                 cn :  [] 
7230             };
7231             if (this.before && typeof(this.before) == 'string') {
7232                 
7233                 inputblock.cn.push({
7234                     tag :'span',
7235                     cls : 'roo-input-before input-group-addon',
7236                     html : this.before
7237                 });
7238             }
7239             if (this.before && typeof(this.before) == 'object') {
7240                 this.before = Roo.factory(this.before);
7241                 Roo.log(this.before);
7242                 inputblock.cn.push({
7243                     tag :'span',
7244                     cls : 'roo-input-before input-group-' +
7245                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7246                 });
7247             }
7248             
7249             inputblock.cn.push(input);
7250             
7251             if (this.after && typeof(this.after) == 'string') {
7252                 inputblock.cn.push({
7253                     tag :'span',
7254                     cls : 'roo-input-after input-group-addon',
7255                     html : this.after
7256                 });
7257             }
7258             if (this.after && typeof(this.after) == 'object') {
7259                 this.after = Roo.factory(this.after);
7260                 Roo.log(this.after);
7261                 inputblock.cn.push({
7262                     tag :'span',
7263                     cls : 'roo-input-after input-group-' +
7264                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7265                 });
7266             }
7267         };
7268         
7269         if (align ==='left' && this.fieldLabel.length) {
7270                 Roo.log("left and has label");
7271                 cfg.cn = [
7272                     
7273                     {
7274                         tag: 'label',
7275                         'for' :  id,
7276                         cls : 'control-label col-sm-' + this.labelWidth,
7277                         html : this.fieldLabel
7278                         
7279                     },
7280                     {
7281                         cls : "col-sm-" + (12 - this.labelWidth), 
7282                         cn: [
7283                             inputblock
7284                         ]
7285                     }
7286                     
7287                 ];
7288         } else if ( this.fieldLabel.length) {
7289                 Roo.log(" label");
7290                  cfg.cn = [
7291                    
7292                     {
7293                         tag: 'label',
7294                         //cls : 'input-group-addon',
7295                         html : this.fieldLabel
7296                         
7297                     },
7298                     
7299                     inputblock
7300                     
7301                 ];
7302
7303         } else {
7304             
7305                 Roo.log(" no label && no align");
7306                 cfg.cn = [
7307                     
7308                         inputblock
7309                     
7310                 ];
7311                 
7312                 
7313         };
7314         Roo.log('input-parentType: ' + this.parentType);
7315         
7316         if (this.parentType === 'Navbar' &&  this.parent().bar) {
7317            cfg.cls += ' navbar-form';
7318            Roo.log(cfg);
7319         }
7320         
7321         return cfg;
7322         
7323     },
7324     /**
7325      * return the real input element.
7326      */
7327     inputEl: function ()
7328     {
7329         return this.el.select('input.form-control',true).first();
7330     },
7331     
7332     tooltipEl : function()
7333     {
7334         return this.inputEl();
7335     },
7336     
7337     setDisabled : function(v)
7338     {
7339         var i  = this.inputEl().dom;
7340         if (!v) {
7341             i.removeAttribute('disabled');
7342             return;
7343             
7344         }
7345         i.setAttribute('disabled','true');
7346     },
7347     initEvents : function()
7348     {
7349           
7350         this.inputEl().on("keydown" , this.fireKey,  this);
7351         this.inputEl().on("focus", this.onFocus,  this);
7352         this.inputEl().on("blur", this.onBlur,  this);
7353         
7354         this.inputEl().relayEvent('keyup', this);
7355
7356         // reference to original value for reset
7357         this.originalValue = this.getValue();
7358         //Roo.form.TextField.superclass.initEvents.call(this);
7359         if(this.validationEvent == 'keyup'){
7360             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
7361             this.inputEl().on('keyup', this.filterValidation, this);
7362         }
7363         else if(this.validationEvent !== false){
7364             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
7365         }
7366         
7367         if(this.selectOnFocus){
7368             this.on("focus", this.preFocus, this);
7369             
7370         }
7371         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
7372             this.inputEl().on("keypress", this.filterKeys, this);
7373         }
7374        /* if(this.grow){
7375             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
7376             this.el.on("click", this.autoSize,  this);
7377         }
7378         */
7379         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
7380             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
7381         }
7382         
7383         if (typeof(this.before) == 'object') {
7384             this.before.render(this.el.select('.roo-input-before',true).first());
7385         }
7386         if (typeof(this.after) == 'object') {
7387             this.after.render(this.el.select('.roo-input-after',true).first());
7388         }
7389         
7390         
7391     },
7392     filterValidation : function(e){
7393         if(!e.isNavKeyPress()){
7394             this.validationTask.delay(this.validationDelay);
7395         }
7396     },
7397      /**
7398      * Validates the field value
7399      * @return {Boolean} True if the value is valid, else false
7400      */
7401     validate : function(){
7402         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
7403         if(this.disabled || this.validateValue(this.getRawValue())){
7404             this.clearInvalid();
7405             return true;
7406         }
7407         return false;
7408     },
7409     
7410     
7411     /**
7412      * Validates a value according to the field's validation rules and marks the field as invalid
7413      * if the validation fails
7414      * @param {Mixed} value The value to validate
7415      * @return {Boolean} True if the value is valid, else false
7416      */
7417     validateValue : function(value){
7418         if(value.length < 1)  { // if it's blank
7419              if(this.allowBlank){
7420                 this.clearInvalid();
7421                 return true;
7422              }else{
7423                 this.markInvalid(this.blankText);
7424                 return false;
7425              }
7426         }
7427         if(value.length < this.minLength){
7428             this.markInvalid(String.format(this.minLengthText, this.minLength));
7429             return false;
7430         }
7431         if(value.length > this.maxLength){
7432             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
7433             return false;
7434         }
7435         if(this.vtype){
7436             var vt = Roo.form.VTypes;
7437             if(!vt[this.vtype](value, this)){
7438                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
7439                 return false;
7440             }
7441         }
7442         if(typeof this.validator == "function"){
7443             var msg = this.validator(value);
7444             if(msg !== true){
7445                 this.markInvalid(msg);
7446                 return false;
7447             }
7448         }
7449         if(this.regex && !this.regex.test(value)){
7450             this.markInvalid(this.regexText);
7451             return false;
7452         }
7453         return true;
7454     },
7455
7456     
7457     
7458      // private
7459     fireKey : function(e){
7460         //Roo.log('field ' + e.getKey());
7461         if(e.isNavKeyPress()){
7462             this.fireEvent("specialkey", this, e);
7463         }
7464     },
7465     focus : function (selectText){
7466         if(this.rendered){
7467             this.inputEl().focus();
7468             if(selectText === true){
7469                 this.inputEl().dom.select();
7470             }
7471         }
7472         return this;
7473     } ,
7474     
7475     onFocus : function(){
7476         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7477            // this.el.addClass(this.focusClass);
7478         }
7479         if(!this.hasFocus){
7480             this.hasFocus = true;
7481             this.startValue = this.getValue();
7482             this.fireEvent("focus", this);
7483         }
7484     },
7485     
7486     beforeBlur : Roo.emptyFn,
7487
7488     
7489     // private
7490     onBlur : function(){
7491         this.beforeBlur();
7492         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7493             //this.el.removeClass(this.focusClass);
7494         }
7495         this.hasFocus = false;
7496         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
7497             this.validate();
7498         }
7499         var v = this.getValue();
7500         if(String(v) !== String(this.startValue)){
7501             this.fireEvent('change', this, v, this.startValue);
7502         }
7503         this.fireEvent("blur", this);
7504     },
7505     
7506     /**
7507      * Resets the current field value to the originally loaded value and clears any validation messages
7508      */
7509     reset : function(){
7510         this.setValue(this.originalValue);
7511         this.clearInvalid();
7512     },
7513      /**
7514      * Returns the name of the field
7515      * @return {Mixed} name The name field
7516      */
7517     getName: function(){
7518         return this.name;
7519     },
7520      /**
7521      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
7522      * @return {Mixed} value The field value
7523      */
7524     getValue : function(){
7525         
7526         var v = this.inputEl().getValue();
7527         
7528         return v;
7529     },
7530     /**
7531      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
7532      * @return {Mixed} value The field value
7533      */
7534     getRawValue : function(){
7535         var v = this.inputEl().getValue();
7536         
7537         return v;
7538     },
7539     
7540     /**
7541      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
7542      * @param {Mixed} value The value to set
7543      */
7544     setRawValue : function(v){
7545         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7546     },
7547     
7548     selectText : function(start, end){
7549         var v = this.getRawValue();
7550         if(v.length > 0){
7551             start = start === undefined ? 0 : start;
7552             end = end === undefined ? v.length : end;
7553             var d = this.inputEl().dom;
7554             if(d.setSelectionRange){
7555                 d.setSelectionRange(start, end);
7556             }else if(d.createTextRange){
7557                 var range = d.createTextRange();
7558                 range.moveStart("character", start);
7559                 range.moveEnd("character", v.length-end);
7560                 range.select();
7561             }
7562         }
7563     },
7564     
7565     /**
7566      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
7567      * @param {Mixed} value The value to set
7568      */
7569     setValue : function(v){
7570         this.value = v;
7571         if(this.rendered){
7572             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7573             this.validate();
7574         }
7575     },
7576     
7577     /*
7578     processValue : function(value){
7579         if(this.stripCharsRe){
7580             var newValue = value.replace(this.stripCharsRe, '');
7581             if(newValue !== value){
7582                 this.setRawValue(newValue);
7583                 return newValue;
7584             }
7585         }
7586         return value;
7587     },
7588   */
7589     preFocus : function(){
7590         
7591         if(this.selectOnFocus){
7592             this.inputEl().dom.select();
7593         }
7594     },
7595     filterKeys : function(e){
7596         var k = e.getKey();
7597         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
7598             return;
7599         }
7600         var c = e.getCharCode(), cc = String.fromCharCode(c);
7601         if(Roo.isIE && (e.isSpecialKey() || !cc)){
7602             return;
7603         }
7604         if(!this.maskRe.test(cc)){
7605             e.stopEvent();
7606         }
7607     },
7608      /**
7609      * Clear any invalid styles/messages for this field
7610      */
7611     clearInvalid : function(){
7612         
7613         if(!this.el || this.preventMark){ // not rendered
7614             return;
7615         }
7616         this.el.removeClass(this.invalidClass);
7617         /*
7618         switch(this.msgTarget){
7619             case 'qtip':
7620                 this.el.dom.qtip = '';
7621                 break;
7622             case 'title':
7623                 this.el.dom.title = '';
7624                 break;
7625             case 'under':
7626                 if(this.errorEl){
7627                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
7628                 }
7629                 break;
7630             case 'side':
7631                 if(this.errorIcon){
7632                     this.errorIcon.dom.qtip = '';
7633                     this.errorIcon.hide();
7634                     this.un('resize', this.alignErrorIcon, this);
7635                 }
7636                 break;
7637             default:
7638                 var t = Roo.getDom(this.msgTarget);
7639                 t.innerHTML = '';
7640                 t.style.display = 'none';
7641                 break;
7642         }
7643         */
7644         this.fireEvent('valid', this);
7645     },
7646      /**
7647      * Mark this field as invalid
7648      * @param {String} msg The validation message
7649      */
7650     markInvalid : function(msg){
7651         if(!this.el  || this.preventMark){ // not rendered
7652             return;
7653         }
7654         this.el.addClass(this.invalidClass);
7655         /*
7656         msg = msg || this.invalidText;
7657         switch(this.msgTarget){
7658             case 'qtip':
7659                 this.el.dom.qtip = msg;
7660                 this.el.dom.qclass = 'x-form-invalid-tip';
7661                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
7662                     Roo.QuickTips.enable();
7663                 }
7664                 break;
7665             case 'title':
7666                 this.el.dom.title = msg;
7667                 break;
7668             case 'under':
7669                 if(!this.errorEl){
7670                     var elp = this.el.findParent('.x-form-element', 5, true);
7671                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
7672                     this.errorEl.setWidth(elp.getWidth(true)-20);
7673                 }
7674                 this.errorEl.update(msg);
7675                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
7676                 break;
7677             case 'side':
7678                 if(!this.errorIcon){
7679                     var elp = this.el.findParent('.x-form-element', 5, true);
7680                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
7681                 }
7682                 this.alignErrorIcon();
7683                 this.errorIcon.dom.qtip = msg;
7684                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
7685                 this.errorIcon.show();
7686                 this.on('resize', this.alignErrorIcon, this);
7687                 break;
7688             default:
7689                 var t = Roo.getDom(this.msgTarget);
7690                 t.innerHTML = msg;
7691                 t.style.display = this.msgDisplay;
7692                 break;
7693         }
7694         */
7695         this.fireEvent('invalid', this, msg);
7696     },
7697     // private
7698     SafariOnKeyDown : function(event)
7699     {
7700         // this is a workaround for a password hang bug on chrome/ webkit.
7701         
7702         var isSelectAll = false;
7703         
7704         if(this.inputEl().dom.selectionEnd > 0){
7705             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
7706         }
7707         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
7708             event.preventDefault();
7709             this.setValue('');
7710             return;
7711         }
7712         
7713         if(isSelectAll  && event.getCharCode() > 31){ // not backspace and delete key
7714             
7715             event.preventDefault();
7716             // this is very hacky as keydown always get's upper case.
7717             //
7718             var cc = String.fromCharCode(event.getCharCode());
7719             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
7720             
7721         }
7722     },
7723     adjustWidth : function(tag, w){
7724         tag = tag.toLowerCase();
7725         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
7726             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
7727                 if(tag == 'input'){
7728                     return w + 2;
7729                 }
7730                 if(tag == 'textarea'){
7731                     return w-2;
7732                 }
7733             }else if(Roo.isOpera){
7734                 if(tag == 'input'){
7735                     return w + 2;
7736                 }
7737                 if(tag == 'textarea'){
7738                     return w-2;
7739                 }
7740             }
7741         }
7742         return w;
7743     }
7744     
7745 });
7746
7747  
7748 /*
7749  * - LGPL
7750  *
7751  * Input
7752  * 
7753  */
7754
7755 /**
7756  * @class Roo.bootstrap.TextArea
7757  * @extends Roo.bootstrap.Input
7758  * Bootstrap TextArea class
7759  * @cfg {Number} cols Specifies the visible width of a text area
7760  * @cfg {Number} rows Specifies the visible number of lines in a text area
7761  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
7762  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
7763  * @cfg {string} html text
7764  * 
7765  * @constructor
7766  * Create a new TextArea
7767  * @param {Object} config The config object
7768  */
7769
7770 Roo.bootstrap.TextArea = function(config){
7771     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
7772    
7773 };
7774
7775 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
7776      
7777     cols : false,
7778     rows : 5,
7779     readOnly : false,
7780     warp : 'soft',
7781     resize : false,
7782     value: false,
7783     html: false,
7784     
7785     getAutoCreate : function(){
7786         
7787         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7788         
7789         var id = Roo.id();
7790         
7791         var cfg = {};
7792         
7793         var input =  {
7794             tag: 'textarea',
7795             id : id,
7796             warp : this.warp,
7797             rows : this.rows,
7798             value : this.value || '',
7799             html: this.html || '',
7800             cls : 'form-control',
7801             placeholder : this.placeholder || '' 
7802             
7803         };
7804         
7805         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7806             input.maxLength = this.maxLength;
7807         }
7808         
7809         if(this.resize){
7810             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
7811         }
7812         
7813         if(this.cols){
7814             input.cols = this.cols;
7815         }
7816         
7817         if (this.readOnly) {
7818             input.readonly = true;
7819         }
7820         
7821         if (this.name) {
7822             input.name = this.name;
7823         }
7824         
7825         if (this.size) {
7826             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
7827         }
7828         
7829         var settings=this;
7830         ['xs','sm','md','lg'].map(function(size){
7831             if (settings[size]) {
7832                 cfg.cls += ' col-' + size + '-' + settings[size];
7833             }
7834         });
7835         
7836         var inputblock = input;
7837         
7838         if (this.before || this.after) {
7839             
7840             inputblock = {
7841                 cls : 'input-group',
7842                 cn :  [] 
7843             };
7844             if (this.before) {
7845                 inputblock.cn.push({
7846                     tag :'span',
7847                     cls : 'input-group-addon',
7848                     html : this.before
7849                 });
7850             }
7851             inputblock.cn.push(input);
7852             if (this.after) {
7853                 inputblock.cn.push({
7854                     tag :'span',
7855                     cls : 'input-group-addon',
7856                     html : this.after
7857                 });
7858             }
7859             
7860         }
7861         
7862         if (align ==='left' && this.fieldLabel.length) {
7863                 Roo.log("left and has label");
7864                 cfg.cn = [
7865                     
7866                     {
7867                         tag: 'label',
7868                         'for' :  id,
7869                         cls : 'control-label col-sm-' + this.labelWidth,
7870                         html : this.fieldLabel
7871                         
7872                     },
7873                     {
7874                         cls : "col-sm-" + (12 - this.labelWidth), 
7875                         cn: [
7876                             inputblock
7877                         ]
7878                     }
7879                     
7880                 ];
7881         } else if ( this.fieldLabel.length) {
7882                 Roo.log(" label");
7883                  cfg.cn = [
7884                    
7885                     {
7886                         tag: 'label',
7887                         //cls : 'input-group-addon',
7888                         html : this.fieldLabel
7889                         
7890                     },
7891                     
7892                     inputblock
7893                     
7894                 ];
7895
7896         } else {
7897             
7898                    Roo.log(" no label && no align");
7899                 cfg.cn = [
7900                     
7901                         inputblock
7902                     
7903                 ];
7904                 
7905                 
7906         }
7907         
7908         if (this.disabled) {
7909             input.disabled=true;
7910         }
7911         
7912         return cfg;
7913         
7914     },
7915     /**
7916      * return the real textarea element.
7917      */
7918     inputEl: function ()
7919     {
7920         return this.el.select('textarea.form-control',true).first();
7921     }
7922 });
7923
7924  
7925 /*
7926  * - LGPL
7927  *
7928  * trigger field - base class for combo..
7929  * 
7930  */
7931  
7932 /**
7933  * @class Roo.bootstrap.TriggerField
7934  * @extends Roo.bootstrap.Input
7935  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
7936  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
7937  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
7938  * for which you can provide a custom implementation.  For example:
7939  * <pre><code>
7940 var trigger = new Roo.bootstrap.TriggerField();
7941 trigger.onTriggerClick = myTriggerFn;
7942 trigger.applyTo('my-field');
7943 </code></pre>
7944  *
7945  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
7946  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
7947  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
7948  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
7949  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
7950
7951  * @constructor
7952  * Create a new TriggerField.
7953  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
7954  * to the base TextField)
7955  */
7956 Roo.bootstrap.TriggerField = function(config){
7957     this.mimicing = false;
7958     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
7959 };
7960
7961 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
7962     /**
7963      * @cfg {String} triggerClass A CSS class to apply to the trigger
7964      */
7965      /**
7966      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
7967      */
7968     hideTrigger:false,
7969
7970     /** @cfg {Boolean} grow @hide */
7971     /** @cfg {Number} growMin @hide */
7972     /** @cfg {Number} growMax @hide */
7973
7974     /**
7975      * @hide 
7976      * @method
7977      */
7978     autoSize: Roo.emptyFn,
7979     // private
7980     monitorTab : true,
7981     // private
7982     deferHeight : true,
7983
7984     
7985     actionMode : 'wrap',
7986     
7987     caret : false,
7988     
7989     
7990     getAutoCreate : function(){
7991        
7992         var align = this.labelAlign || this.parentLabelAlign();
7993         
7994         var id = Roo.id();
7995         
7996         var cfg = {
7997             cls: 'form-group' //input-group
7998         };
7999         
8000         
8001         var input =  {
8002             tag: 'input',
8003             id : id,
8004             type : this.inputType,
8005             cls : 'form-control',
8006             autocomplete: 'false',
8007             placeholder : this.placeholder || '' 
8008             
8009         };
8010         if (this.name) {
8011             input.name = this.name;
8012         }
8013         if (this.size) {
8014             input.cls += ' input-' + this.size;
8015         }
8016         
8017         if (this.disabled) {
8018             input.disabled=true;
8019         }
8020         
8021         var inputblock = input;
8022         
8023         if (this.before || this.after) {
8024             
8025             inputblock = {
8026                 cls : 'input-group',
8027                 cn :  [] 
8028             };
8029             if (this.before) {
8030                 inputblock.cn.push({
8031                     tag :'span',
8032                     cls : 'input-group-addon',
8033                     html : this.before
8034                 });
8035             }
8036             inputblock.cn.push(input);
8037             if (this.after) {
8038                 inputblock.cn.push({
8039                     tag :'span',
8040                     cls : 'input-group-addon',
8041                     html : this.after
8042                 });
8043             }
8044             
8045         };
8046         
8047         var box = {
8048             tag: 'div',
8049             cn: [
8050                 {
8051                     tag: 'input',
8052                     type : 'hidden',
8053                     cls: 'form-hidden-field'
8054                 },
8055                 inputblock
8056             ]
8057             
8058         };
8059         
8060         if(this.multiple){
8061             Roo.log('multiple');
8062             
8063             box = {
8064                 tag: 'div',
8065                 cn: [
8066                     {
8067                         tag: 'input',
8068                         type : 'hidden',
8069                         cls: 'form-hidden-field'
8070                     },
8071                     {
8072                         tag: 'ul',
8073                         cls: 'select2-choices',
8074                         cn:[
8075                             {
8076                                 tag: 'li',
8077                                 cls: 'select2-search-field',
8078                                 cn: [
8079
8080                                     inputblock
8081                                 ]
8082                             }
8083                         ]
8084                     }
8085                 ]
8086             }
8087         };
8088         
8089         var combobox = {
8090             cls: 'select2-container input-group',
8091             cn: [
8092                 box
8093 //                {
8094 //                    tag: 'ul',
8095 //                    cls: 'typeahead typeahead-long dropdown-menu',
8096 //                    style: 'display:none'
8097 //                }
8098             ]
8099         };
8100         
8101         if(!this.multiple && this.showToggleBtn){
8102             
8103             var caret = {
8104                         tag: 'span',
8105                         cls: 'caret'
8106              };
8107             if (this.caret != false) {
8108                 caret = {
8109                      tag: 'i',
8110                      cls: 'fa fa-' + this.caret
8111                 };
8112                 
8113             }
8114             
8115             combobox.cn.push({
8116                 tag :'span',
8117                 cls : 'input-group-addon btn dropdown-toggle',
8118                 cn : [
8119                     caret,
8120                     {
8121                         tag: 'span',
8122                         cls: 'combobox-clear',
8123                         cn  : [
8124                             {
8125                                 tag : 'i',
8126                                 cls: 'icon-remove'
8127                             }
8128                         ]
8129                     }
8130                 ]
8131
8132             })
8133         }
8134         
8135         if(this.multiple){
8136             combobox.cls += ' select2-container-multi';
8137         }
8138         
8139         if (align ==='left' && this.fieldLabel.length) {
8140             
8141                 Roo.log("left and has label");
8142                 cfg.cn = [
8143                     
8144                     {
8145                         tag: 'label',
8146                         'for' :  id,
8147                         cls : 'control-label col-sm-' + this.labelWidth,
8148                         html : this.fieldLabel
8149                         
8150                     },
8151                     {
8152                         cls : "col-sm-" + (12 - this.labelWidth), 
8153                         cn: [
8154                             combobox
8155                         ]
8156                     }
8157                     
8158                 ];
8159         } else if ( this.fieldLabel.length) {
8160                 Roo.log(" label");
8161                  cfg.cn = [
8162                    
8163                     {
8164                         tag: 'label',
8165                         //cls : 'input-group-addon',
8166                         html : this.fieldLabel
8167                         
8168                     },
8169                     
8170                     combobox
8171                     
8172                 ];
8173
8174         } else {
8175             
8176                 Roo.log(" no label && no align");
8177                 cfg = combobox
8178                      
8179                 
8180         }
8181          
8182         var settings=this;
8183         ['xs','sm','md','lg'].map(function(size){
8184             if (settings[size]) {
8185                 cfg.cls += ' col-' + size + '-' + settings[size];
8186             }
8187         });
8188         
8189         return cfg;
8190         
8191     },
8192     
8193     
8194     
8195     // private
8196     onResize : function(w, h){
8197 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
8198 //        if(typeof w == 'number'){
8199 //            var x = w - this.trigger.getWidth();
8200 //            this.inputEl().setWidth(this.adjustWidth('input', x));
8201 //            this.trigger.setStyle('left', x+'px');
8202 //        }
8203     },
8204
8205     // private
8206     adjustSize : Roo.BoxComponent.prototype.adjustSize,
8207
8208     // private
8209     getResizeEl : function(){
8210         return this.inputEl();
8211     },
8212
8213     // private
8214     getPositionEl : function(){
8215         return this.inputEl();
8216     },
8217
8218     // private
8219     alignErrorIcon : function(){
8220         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
8221     },
8222
8223     // private
8224     initEvents : function(){
8225         
8226         this.createList();
8227         
8228         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
8229         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
8230         if(!this.multiple && this.showToggleBtn){
8231             this.trigger = this.el.select('span.dropdown-toggle',true).first();
8232             if(this.hideTrigger){
8233                 this.trigger.setDisplayed(false);
8234             }
8235             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
8236         }
8237         
8238         if(this.multiple){
8239             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
8240         }
8241         
8242         //this.trigger.addClassOnOver('x-form-trigger-over');
8243         //this.trigger.addClassOnClick('x-form-trigger-click');
8244         
8245         //if(!this.width){
8246         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
8247         //}
8248     },
8249     
8250     createList : function()
8251     {
8252         this.list = Roo.get(document.body).createChild({
8253             tag: 'ul',
8254             cls: 'typeahead typeahead-long dropdown-menu',
8255             style: 'display:none'
8256         });
8257         
8258         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
8259         
8260     },
8261
8262     // private
8263     initTrigger : function(){
8264        
8265     },
8266
8267     // private
8268     onDestroy : function(){
8269         if(this.trigger){
8270             this.trigger.removeAllListeners();
8271           //  this.trigger.remove();
8272         }
8273         //if(this.wrap){
8274         //    this.wrap.remove();
8275         //}
8276         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
8277     },
8278
8279     // private
8280     onFocus : function(){
8281         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
8282         /*
8283         if(!this.mimicing){
8284             this.wrap.addClass('x-trigger-wrap-focus');
8285             this.mimicing = true;
8286             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
8287             if(this.monitorTab){
8288                 this.el.on("keydown", this.checkTab, this);
8289             }
8290         }
8291         */
8292     },
8293
8294     // private
8295     checkTab : function(e){
8296         if(e.getKey() == e.TAB){
8297             this.triggerBlur();
8298         }
8299     },
8300
8301     // private
8302     onBlur : function(){
8303         // do nothing
8304     },
8305
8306     // private
8307     mimicBlur : function(e, t){
8308         /*
8309         if(!this.wrap.contains(t) && this.validateBlur()){
8310             this.triggerBlur();
8311         }
8312         */
8313     },
8314
8315     // private
8316     triggerBlur : function(){
8317         this.mimicing = false;
8318         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
8319         if(this.monitorTab){
8320             this.el.un("keydown", this.checkTab, this);
8321         }
8322         //this.wrap.removeClass('x-trigger-wrap-focus');
8323         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
8324     },
8325
8326     // private
8327     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
8328     validateBlur : function(e, t){
8329         return true;
8330     },
8331
8332     // private
8333     onDisable : function(){
8334         this.inputEl().dom.disabled = true;
8335         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
8336         //if(this.wrap){
8337         //    this.wrap.addClass('x-item-disabled');
8338         //}
8339     },
8340
8341     // private
8342     onEnable : function(){
8343         this.inputEl().dom.disabled = false;
8344         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
8345         //if(this.wrap){
8346         //    this.el.removeClass('x-item-disabled');
8347         //}
8348     },
8349
8350     // private
8351     onShow : function(){
8352         var ae = this.getActionEl();
8353         
8354         if(ae){
8355             ae.dom.style.display = '';
8356             ae.dom.style.visibility = 'visible';
8357         }
8358     },
8359
8360     // private
8361     
8362     onHide : function(){
8363         var ae = this.getActionEl();
8364         ae.dom.style.display = 'none';
8365     },
8366
8367     /**
8368      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
8369      * by an implementing function.
8370      * @method
8371      * @param {EventObject} e
8372      */
8373     onTriggerClick : Roo.emptyFn
8374 });
8375  /*
8376  * Based on:
8377  * Ext JS Library 1.1.1
8378  * Copyright(c) 2006-2007, Ext JS, LLC.
8379  *
8380  * Originally Released Under LGPL - original licence link has changed is not relivant.
8381  *
8382  * Fork - LGPL
8383  * <script type="text/javascript">
8384  */
8385
8386
8387 /**
8388  * @class Roo.data.SortTypes
8389  * @singleton
8390  * Defines the default sorting (casting?) comparison functions used when sorting data.
8391  */
8392 Roo.data.SortTypes = {
8393     /**
8394      * Default sort that does nothing
8395      * @param {Mixed} s The value being converted
8396      * @return {Mixed} The comparison value
8397      */
8398     none : function(s){
8399         return s;
8400     },
8401     
8402     /**
8403      * The regular expression used to strip tags
8404      * @type {RegExp}
8405      * @property
8406      */
8407     stripTagsRE : /<\/?[^>]+>/gi,
8408     
8409     /**
8410      * Strips all HTML tags to sort on text only
8411      * @param {Mixed} s The value being converted
8412      * @return {String} The comparison value
8413      */
8414     asText : function(s){
8415         return String(s).replace(this.stripTagsRE, "");
8416     },
8417     
8418     /**
8419      * Strips all HTML tags to sort on text only - Case insensitive
8420      * @param {Mixed} s The value being converted
8421      * @return {String} The comparison value
8422      */
8423     asUCText : function(s){
8424         return String(s).toUpperCase().replace(this.stripTagsRE, "");
8425     },
8426     
8427     /**
8428      * Case insensitive string
8429      * @param {Mixed} s The value being converted
8430      * @return {String} The comparison value
8431      */
8432     asUCString : function(s) {
8433         return String(s).toUpperCase();
8434     },
8435     
8436     /**
8437      * Date sorting
8438      * @param {Mixed} s The value being converted
8439      * @return {Number} The comparison value
8440      */
8441     asDate : function(s) {
8442         if(!s){
8443             return 0;
8444         }
8445         if(s instanceof Date){
8446             return s.getTime();
8447         }
8448         return Date.parse(String(s));
8449     },
8450     
8451     /**
8452      * Float sorting
8453      * @param {Mixed} s The value being converted
8454      * @return {Float} The comparison value
8455      */
8456     asFloat : function(s) {
8457         var val = parseFloat(String(s).replace(/,/g, ""));
8458         if(isNaN(val)) val = 0;
8459         return val;
8460     },
8461     
8462     /**
8463      * Integer sorting
8464      * @param {Mixed} s The value being converted
8465      * @return {Number} The comparison value
8466      */
8467     asInt : function(s) {
8468         var val = parseInt(String(s).replace(/,/g, ""));
8469         if(isNaN(val)) val = 0;
8470         return val;
8471     }
8472 };/*
8473  * Based on:
8474  * Ext JS Library 1.1.1
8475  * Copyright(c) 2006-2007, Ext JS, LLC.
8476  *
8477  * Originally Released Under LGPL - original licence link has changed is not relivant.
8478  *
8479  * Fork - LGPL
8480  * <script type="text/javascript">
8481  */
8482
8483 /**
8484 * @class Roo.data.Record
8485  * Instances of this class encapsulate both record <em>definition</em> information, and record
8486  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
8487  * to access Records cached in an {@link Roo.data.Store} object.<br>
8488  * <p>
8489  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
8490  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
8491  * objects.<br>
8492  * <p>
8493  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
8494  * @constructor
8495  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
8496  * {@link #create}. The parameters are the same.
8497  * @param {Array} data An associative Array of data values keyed by the field name.
8498  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
8499  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
8500  * not specified an integer id is generated.
8501  */
8502 Roo.data.Record = function(data, id){
8503     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
8504     this.data = data;
8505 };
8506
8507 /**
8508  * Generate a constructor for a specific record layout.
8509  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
8510  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
8511  * Each field definition object may contain the following properties: <ul>
8512  * <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,
8513  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
8514  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
8515  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
8516  * is being used, then this is a string containing the javascript expression to reference the data relative to 
8517  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
8518  * to the data item relative to the record element. If the mapping expression is the same as the field name,
8519  * this may be omitted.</p></li>
8520  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
8521  * <ul><li>auto (Default, implies no conversion)</li>
8522  * <li>string</li>
8523  * <li>int</li>
8524  * <li>float</li>
8525  * <li>boolean</li>
8526  * <li>date</li></ul></p></li>
8527  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
8528  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
8529  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
8530  * by the Reader into an object that will be stored in the Record. It is passed the
8531  * following parameters:<ul>
8532  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
8533  * </ul></p></li>
8534  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
8535  * </ul>
8536  * <br>usage:<br><pre><code>
8537 var TopicRecord = Roo.data.Record.create(
8538     {name: 'title', mapping: 'topic_title'},
8539     {name: 'author', mapping: 'username'},
8540     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
8541     {name: 'lastPost', mapping: 'post_time', type: 'date'},
8542     {name: 'lastPoster', mapping: 'user2'},
8543     {name: 'excerpt', mapping: 'post_text'}
8544 );
8545
8546 var myNewRecord = new TopicRecord({
8547     title: 'Do my job please',
8548     author: 'noobie',
8549     totalPosts: 1,
8550     lastPost: new Date(),
8551     lastPoster: 'Animal',
8552     excerpt: 'No way dude!'
8553 });
8554 myStore.add(myNewRecord);
8555 </code></pre>
8556  * @method create
8557  * @static
8558  */
8559 Roo.data.Record.create = function(o){
8560     var f = function(){
8561         f.superclass.constructor.apply(this, arguments);
8562     };
8563     Roo.extend(f, Roo.data.Record);
8564     var p = f.prototype;
8565     p.fields = new Roo.util.MixedCollection(false, function(field){
8566         return field.name;
8567     });
8568     for(var i = 0, len = o.length; i < len; i++){
8569         p.fields.add(new Roo.data.Field(o[i]));
8570     }
8571     f.getField = function(name){
8572         return p.fields.get(name);  
8573     };
8574     return f;
8575 };
8576
8577 Roo.data.Record.AUTO_ID = 1000;
8578 Roo.data.Record.EDIT = 'edit';
8579 Roo.data.Record.REJECT = 'reject';
8580 Roo.data.Record.COMMIT = 'commit';
8581
8582 Roo.data.Record.prototype = {
8583     /**
8584      * Readonly flag - true if this record has been modified.
8585      * @type Boolean
8586      */
8587     dirty : false,
8588     editing : false,
8589     error: null,
8590     modified: null,
8591
8592     // private
8593     join : function(store){
8594         this.store = store;
8595     },
8596
8597     /**
8598      * Set the named field to the specified value.
8599      * @param {String} name The name of the field to set.
8600      * @param {Object} value The value to set the field to.
8601      */
8602     set : function(name, value){
8603         if(this.data[name] == value){
8604             return;
8605         }
8606         this.dirty = true;
8607         if(!this.modified){
8608             this.modified = {};
8609         }
8610         if(typeof this.modified[name] == 'undefined'){
8611             this.modified[name] = this.data[name];
8612         }
8613         this.data[name] = value;
8614         if(!this.editing && this.store){
8615             this.store.afterEdit(this);
8616         }       
8617     },
8618
8619     /**
8620      * Get the value of the named field.
8621      * @param {String} name The name of the field to get the value of.
8622      * @return {Object} The value of the field.
8623      */
8624     get : function(name){
8625         return this.data[name]; 
8626     },
8627
8628     // private
8629     beginEdit : function(){
8630         this.editing = true;
8631         this.modified = {}; 
8632     },
8633
8634     // private
8635     cancelEdit : function(){
8636         this.editing = false;
8637         delete this.modified;
8638     },
8639
8640     // private
8641     endEdit : function(){
8642         this.editing = false;
8643         if(this.dirty && this.store){
8644             this.store.afterEdit(this);
8645         }
8646     },
8647
8648     /**
8649      * Usually called by the {@link Roo.data.Store} which owns the Record.
8650      * Rejects all changes made to the Record since either creation, or the last commit operation.
8651      * Modified fields are reverted to their original values.
8652      * <p>
8653      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8654      * of reject operations.
8655      */
8656     reject : function(){
8657         var m = this.modified;
8658         for(var n in m){
8659             if(typeof m[n] != "function"){
8660                 this.data[n] = m[n];
8661             }
8662         }
8663         this.dirty = false;
8664         delete this.modified;
8665         this.editing = false;
8666         if(this.store){
8667             this.store.afterReject(this);
8668         }
8669     },
8670
8671     /**
8672      * Usually called by the {@link Roo.data.Store} which owns the Record.
8673      * Commits all changes made to the Record since either creation, or the last commit operation.
8674      * <p>
8675      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8676      * of commit operations.
8677      */
8678     commit : function(){
8679         this.dirty = false;
8680         delete this.modified;
8681         this.editing = false;
8682         if(this.store){
8683             this.store.afterCommit(this);
8684         }
8685     },
8686
8687     // private
8688     hasError : function(){
8689         return this.error != null;
8690     },
8691
8692     // private
8693     clearError : function(){
8694         this.error = null;
8695     },
8696
8697     /**
8698      * Creates a copy of this record.
8699      * @param {String} id (optional) A new record id if you don't want to use this record's id
8700      * @return {Record}
8701      */
8702     copy : function(newId) {
8703         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
8704     }
8705 };/*
8706  * Based on:
8707  * Ext JS Library 1.1.1
8708  * Copyright(c) 2006-2007, Ext JS, LLC.
8709  *
8710  * Originally Released Under LGPL - original licence link has changed is not relivant.
8711  *
8712  * Fork - LGPL
8713  * <script type="text/javascript">
8714  */
8715
8716
8717
8718 /**
8719  * @class Roo.data.Store
8720  * @extends Roo.util.Observable
8721  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
8722  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
8723  * <p>
8724  * 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
8725  * has no knowledge of the format of the data returned by the Proxy.<br>
8726  * <p>
8727  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
8728  * instances from the data object. These records are cached and made available through accessor functions.
8729  * @constructor
8730  * Creates a new Store.
8731  * @param {Object} config A config object containing the objects needed for the Store to access data,
8732  * and read the data into Records.
8733  */
8734 Roo.data.Store = function(config){
8735     this.data = new Roo.util.MixedCollection(false);
8736     this.data.getKey = function(o){
8737         return o.id;
8738     };
8739     this.baseParams = {};
8740     // private
8741     this.paramNames = {
8742         "start" : "start",
8743         "limit" : "limit",
8744         "sort" : "sort",
8745         "dir" : "dir",
8746         "multisort" : "_multisort"
8747     };
8748
8749     if(config && config.data){
8750         this.inlineData = config.data;
8751         delete config.data;
8752     }
8753
8754     Roo.apply(this, config);
8755     
8756     if(this.reader){ // reader passed
8757         this.reader = Roo.factory(this.reader, Roo.data);
8758         this.reader.xmodule = this.xmodule || false;
8759         if(!this.recordType){
8760             this.recordType = this.reader.recordType;
8761         }
8762         if(this.reader.onMetaChange){
8763             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
8764         }
8765     }
8766
8767     if(this.recordType){
8768         this.fields = this.recordType.prototype.fields;
8769     }
8770     this.modified = [];
8771
8772     this.addEvents({
8773         /**
8774          * @event datachanged
8775          * Fires when the data cache has changed, and a widget which is using this Store
8776          * as a Record cache should refresh its view.
8777          * @param {Store} this
8778          */
8779         datachanged : true,
8780         /**
8781          * @event metachange
8782          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
8783          * @param {Store} this
8784          * @param {Object} meta The JSON metadata
8785          */
8786         metachange : true,
8787         /**
8788          * @event add
8789          * Fires when Records have been added to the Store
8790          * @param {Store} this
8791          * @param {Roo.data.Record[]} records The array of Records added
8792          * @param {Number} index The index at which the record(s) were added
8793          */
8794         add : true,
8795         /**
8796          * @event remove
8797          * Fires when a Record has been removed from the Store
8798          * @param {Store} this
8799          * @param {Roo.data.Record} record The Record that was removed
8800          * @param {Number} index The index at which the record was removed
8801          */
8802         remove : true,
8803         /**
8804          * @event update
8805          * Fires when a Record has been updated
8806          * @param {Store} this
8807          * @param {Roo.data.Record} record The Record that was updated
8808          * @param {String} operation The update operation being performed.  Value may be one of:
8809          * <pre><code>
8810  Roo.data.Record.EDIT
8811  Roo.data.Record.REJECT
8812  Roo.data.Record.COMMIT
8813          * </code></pre>
8814          */
8815         update : true,
8816         /**
8817          * @event clear
8818          * Fires when the data cache has been cleared.
8819          * @param {Store} this
8820          */
8821         clear : true,
8822         /**
8823          * @event beforeload
8824          * Fires before a request is made for a new data object.  If the beforeload handler returns false
8825          * the load action will be canceled.
8826          * @param {Store} this
8827          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8828          */
8829         beforeload : true,
8830         /**
8831          * @event beforeloadadd
8832          * Fires after a new set of Records has been loaded.
8833          * @param {Store} this
8834          * @param {Roo.data.Record[]} records The Records that were loaded
8835          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8836          */
8837         beforeloadadd : true,
8838         /**
8839          * @event load
8840          * Fires after a new set of Records has been loaded, before they are added to the store.
8841          * @param {Store} this
8842          * @param {Roo.data.Record[]} records The Records that were loaded
8843          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8844          * @params {Object} return from reader
8845          */
8846         load : true,
8847         /**
8848          * @event loadexception
8849          * Fires if an exception occurs in the Proxy during loading.
8850          * Called with the signature of the Proxy's "loadexception" event.
8851          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
8852          * 
8853          * @param {Proxy} 
8854          * @param {Object} return from JsonData.reader() - success, totalRecords, records
8855          * @param {Object} load options 
8856          * @param {Object} jsonData from your request (normally this contains the Exception)
8857          */
8858         loadexception : true
8859     });
8860     
8861     if(this.proxy){
8862         this.proxy = Roo.factory(this.proxy, Roo.data);
8863         this.proxy.xmodule = this.xmodule || false;
8864         this.relayEvents(this.proxy,  ["loadexception"]);
8865     }
8866     this.sortToggle = {};
8867     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
8868
8869     Roo.data.Store.superclass.constructor.call(this);
8870
8871     if(this.inlineData){
8872         this.loadData(this.inlineData);
8873         delete this.inlineData;
8874     }
8875 };
8876
8877 Roo.extend(Roo.data.Store, Roo.util.Observable, {
8878      /**
8879     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
8880     * without a remote query - used by combo/forms at present.
8881     */
8882     
8883     /**
8884     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
8885     */
8886     /**
8887     * @cfg {Array} data Inline data to be loaded when the store is initialized.
8888     */
8889     /**
8890     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
8891     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
8892     */
8893     /**
8894     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
8895     * on any HTTP request
8896     */
8897     /**
8898     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
8899     */
8900     /**
8901     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
8902     */
8903     multiSort: false,
8904     /**
8905     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
8906     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
8907     */
8908     remoteSort : false,
8909
8910     /**
8911     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
8912      * loaded or when a record is removed. (defaults to false).
8913     */
8914     pruneModifiedRecords : false,
8915
8916     // private
8917     lastOptions : null,
8918
8919     /**
8920      * Add Records to the Store and fires the add event.
8921      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8922      */
8923     add : function(records){
8924         records = [].concat(records);
8925         for(var i = 0, len = records.length; i < len; i++){
8926             records[i].join(this);
8927         }
8928         var index = this.data.length;
8929         this.data.addAll(records);
8930         this.fireEvent("add", this, records, index);
8931     },
8932
8933     /**
8934      * Remove a Record from the Store and fires the remove event.
8935      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
8936      */
8937     remove : function(record){
8938         var index = this.data.indexOf(record);
8939         this.data.removeAt(index);
8940         if(this.pruneModifiedRecords){
8941             this.modified.remove(record);
8942         }
8943         this.fireEvent("remove", this, record, index);
8944     },
8945
8946     /**
8947      * Remove all Records from the Store and fires the clear event.
8948      */
8949     removeAll : function(){
8950         this.data.clear();
8951         if(this.pruneModifiedRecords){
8952             this.modified = [];
8953         }
8954         this.fireEvent("clear", this);
8955     },
8956
8957     /**
8958      * Inserts Records to the Store at the given index and fires the add event.
8959      * @param {Number} index The start index at which to insert the passed Records.
8960      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8961      */
8962     insert : function(index, records){
8963         records = [].concat(records);
8964         for(var i = 0, len = records.length; i < len; i++){
8965             this.data.insert(index, records[i]);
8966             records[i].join(this);
8967         }
8968         this.fireEvent("add", this, records, index);
8969     },
8970
8971     /**
8972      * Get the index within the cache of the passed Record.
8973      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
8974      * @return {Number} The index of the passed Record. Returns -1 if not found.
8975      */
8976     indexOf : function(record){
8977         return this.data.indexOf(record);
8978     },
8979
8980     /**
8981      * Get the index within the cache of the Record with the passed id.
8982      * @param {String} id The id of the Record to find.
8983      * @return {Number} The index of the Record. Returns -1 if not found.
8984      */
8985     indexOfId : function(id){
8986         return this.data.indexOfKey(id);
8987     },
8988
8989     /**
8990      * Get the Record with the specified id.
8991      * @param {String} id The id of the Record to find.
8992      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
8993      */
8994     getById : function(id){
8995         return this.data.key(id);
8996     },
8997
8998     /**
8999      * Get the Record at the specified index.
9000      * @param {Number} index The index of the Record to find.
9001      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
9002      */
9003     getAt : function(index){
9004         return this.data.itemAt(index);
9005     },
9006
9007     /**
9008      * Returns a range of Records between specified indices.
9009      * @param {Number} startIndex (optional) The starting index (defaults to 0)
9010      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
9011      * @return {Roo.data.Record[]} An array of Records
9012      */
9013     getRange : function(start, end){
9014         return this.data.getRange(start, end);
9015     },
9016
9017     // private
9018     storeOptions : function(o){
9019         o = Roo.apply({}, o);
9020         delete o.callback;
9021         delete o.scope;
9022         this.lastOptions = o;
9023     },
9024
9025     /**
9026      * Loads the Record cache from the configured Proxy using the configured Reader.
9027      * <p>
9028      * If using remote paging, then the first load call must specify the <em>start</em>
9029      * and <em>limit</em> properties in the options.params property to establish the initial
9030      * position within the dataset, and the number of Records to cache on each read from the Proxy.
9031      * <p>
9032      * <strong>It is important to note that for remote data sources, loading is asynchronous,
9033      * and this call will return before the new data has been loaded. Perform any post-processing
9034      * in a callback function, or in a "load" event handler.</strong>
9035      * <p>
9036      * @param {Object} options An object containing properties which control loading options:<ul>
9037      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
9038      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
9039      * passed the following arguments:<ul>
9040      * <li>r : Roo.data.Record[]</li>
9041      * <li>options: Options object from the load call</li>
9042      * <li>success: Boolean success indicator</li></ul></li>
9043      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
9044      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
9045      * </ul>
9046      */
9047     load : function(options){
9048         options = options || {};
9049         if(this.fireEvent("beforeload", this, options) !== false){
9050             this.storeOptions(options);
9051             var p = Roo.apply(options.params || {}, this.baseParams);
9052             // if meta was not loaded from remote source.. try requesting it.
9053             if (!this.reader.metaFromRemote) {
9054                 p._requestMeta = 1;
9055             }
9056             if(this.sortInfo && this.remoteSort){
9057                 var pn = this.paramNames;
9058                 p[pn["sort"]] = this.sortInfo.field;
9059                 p[pn["dir"]] = this.sortInfo.direction;
9060             }
9061             if (this.multiSort) {
9062                 var pn = this.paramNames;
9063                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
9064             }
9065             
9066             this.proxy.load(p, this.reader, this.loadRecords, this, options);
9067         }
9068     },
9069
9070     /**
9071      * Reloads the Record cache from the configured Proxy using the configured Reader and
9072      * the options from the last load operation performed.
9073      * @param {Object} options (optional) An object containing properties which may override the options
9074      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
9075      * the most recently used options are reused).
9076      */
9077     reload : function(options){
9078         this.load(Roo.applyIf(options||{}, this.lastOptions));
9079     },
9080
9081     // private
9082     // Called as a callback by the Reader during a load operation.
9083     loadRecords : function(o, options, success){
9084         if(!o || success === false){
9085             if(success !== false){
9086                 this.fireEvent("load", this, [], options, o);
9087             }
9088             if(options.callback){
9089                 options.callback.call(options.scope || this, [], options, false);
9090             }
9091             return;
9092         }
9093         // if data returned failure - throw an exception.
9094         if (o.success === false) {
9095             // show a message if no listener is registered.
9096             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
9097                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
9098             }
9099             // loadmask wil be hooked into this..
9100             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
9101             return;
9102         }
9103         var r = o.records, t = o.totalRecords || r.length;
9104         
9105         this.fireEvent("beforeloadadd", this, r, options, o);
9106         
9107         if(!options || options.add !== true){
9108             if(this.pruneModifiedRecords){
9109                 this.modified = [];
9110             }
9111             for(var i = 0, len = r.length; i < len; i++){
9112                 r[i].join(this);
9113             }
9114             if(this.snapshot){
9115                 this.data = this.snapshot;
9116                 delete this.snapshot;
9117             }
9118             this.data.clear();
9119             this.data.addAll(r);
9120             this.totalLength = t;
9121             this.applySort();
9122             this.fireEvent("datachanged", this);
9123         }else{
9124             this.totalLength = Math.max(t, this.data.length+r.length);
9125             this.add(r);
9126         }
9127         this.fireEvent("load", this, r, options, o);
9128         if(options.callback){
9129             options.callback.call(options.scope || this, r, options, true);
9130         }
9131     },
9132
9133
9134     /**
9135      * Loads data from a passed data block. A Reader which understands the format of the data
9136      * must have been configured in the constructor.
9137      * @param {Object} data The data block from which to read the Records.  The format of the data expected
9138      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
9139      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
9140      */
9141     loadData : function(o, append){
9142         var r = this.reader.readRecords(o);
9143         this.loadRecords(r, {add: append}, true);
9144     },
9145
9146     /**
9147      * Gets the number of cached records.
9148      * <p>
9149      * <em>If using paging, this may not be the total size of the dataset. If the data object
9150      * used by the Reader contains the dataset size, then the getTotalCount() function returns
9151      * the data set size</em>
9152      */
9153     getCount : function(){
9154         return this.data.length || 0;
9155     },
9156
9157     /**
9158      * Gets the total number of records in the dataset as returned by the server.
9159      * <p>
9160      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
9161      * the dataset size</em>
9162      */
9163     getTotalCount : function(){
9164         return this.totalLength || 0;
9165     },
9166
9167     /**
9168      * Returns the sort state of the Store as an object with two properties:
9169      * <pre><code>
9170  field {String} The name of the field by which the Records are sorted
9171  direction {String} The sort order, "ASC" or "DESC"
9172      * </code></pre>
9173      */
9174     getSortState : function(){
9175         return this.sortInfo;
9176     },
9177
9178     // private
9179     applySort : function(){
9180         if(this.sortInfo && !this.remoteSort){
9181             var s = this.sortInfo, f = s.field;
9182             var st = this.fields.get(f).sortType;
9183             var fn = function(r1, r2){
9184                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
9185                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
9186             };
9187             this.data.sort(s.direction, fn);
9188             if(this.snapshot && this.snapshot != this.data){
9189                 this.snapshot.sort(s.direction, fn);
9190             }
9191         }
9192     },
9193
9194     /**
9195      * Sets the default sort column and order to be used by the next load operation.
9196      * @param {String} fieldName The name of the field to sort by.
9197      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9198      */
9199     setDefaultSort : function(field, dir){
9200         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
9201     },
9202
9203     /**
9204      * Sort the Records.
9205      * If remote sorting is used, the sort is performed on the server, and the cache is
9206      * reloaded. If local sorting is used, the cache is sorted internally.
9207      * @param {String} fieldName The name of the field to sort by.
9208      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9209      */
9210     sort : function(fieldName, dir){
9211         var f = this.fields.get(fieldName);
9212         if(!dir){
9213             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
9214             
9215             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
9216                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
9217             }else{
9218                 dir = f.sortDir;
9219             }
9220         }
9221         this.sortToggle[f.name] = dir;
9222         this.sortInfo = {field: f.name, direction: dir};
9223         if(!this.remoteSort){
9224             this.applySort();
9225             this.fireEvent("datachanged", this);
9226         }else{
9227             this.load(this.lastOptions);
9228         }
9229     },
9230
9231     /**
9232      * Calls the specified function for each of the Records in the cache.
9233      * @param {Function} fn The function to call. The Record is passed as the first parameter.
9234      * Returning <em>false</em> aborts and exits the iteration.
9235      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
9236      */
9237     each : function(fn, scope){
9238         this.data.each(fn, scope);
9239     },
9240
9241     /**
9242      * Gets all records modified since the last commit.  Modified records are persisted across load operations
9243      * (e.g., during paging).
9244      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
9245      */
9246     getModifiedRecords : function(){
9247         return this.modified;
9248     },
9249
9250     // private
9251     createFilterFn : function(property, value, anyMatch){
9252         if(!value.exec){ // not a regex
9253             value = String(value);
9254             if(value.length == 0){
9255                 return false;
9256             }
9257             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
9258         }
9259         return function(r){
9260             return value.test(r.data[property]);
9261         };
9262     },
9263
9264     /**
9265      * Sums the value of <i>property</i> for each record between start and end and returns the result.
9266      * @param {String} property A field on your records
9267      * @param {Number} start The record index to start at (defaults to 0)
9268      * @param {Number} end The last record index to include (defaults to length - 1)
9269      * @return {Number} The sum
9270      */
9271     sum : function(property, start, end){
9272         var rs = this.data.items, v = 0;
9273         start = start || 0;
9274         end = (end || end === 0) ? end : rs.length-1;
9275
9276         for(var i = start; i <= end; i++){
9277             v += (rs[i].data[property] || 0);
9278         }
9279         return v;
9280     },
9281
9282     /**
9283      * Filter the records by a specified property.
9284      * @param {String} field A field on your records
9285      * @param {String/RegExp} value Either a string that the field
9286      * should start with or a RegExp to test against the field
9287      * @param {Boolean} anyMatch True to match any part not just the beginning
9288      */
9289     filter : function(property, value, anyMatch){
9290         var fn = this.createFilterFn(property, value, anyMatch);
9291         return fn ? this.filterBy(fn) : this.clearFilter();
9292     },
9293
9294     /**
9295      * Filter by a function. The specified function will be called with each
9296      * record in this data source. If the function returns true the record is included,
9297      * otherwise it is filtered.
9298      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9299      * @param {Object} scope (optional) The scope of the function (defaults to this)
9300      */
9301     filterBy : function(fn, scope){
9302         this.snapshot = this.snapshot || this.data;
9303         this.data = this.queryBy(fn, scope||this);
9304         this.fireEvent("datachanged", this);
9305     },
9306
9307     /**
9308      * Query the records by a specified property.
9309      * @param {String} field A field on your records
9310      * @param {String/RegExp} value Either a string that the field
9311      * should start with or a RegExp to test against the field
9312      * @param {Boolean} anyMatch True to match any part not just the beginning
9313      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9314      */
9315     query : function(property, value, anyMatch){
9316         var fn = this.createFilterFn(property, value, anyMatch);
9317         return fn ? this.queryBy(fn) : this.data.clone();
9318     },
9319
9320     /**
9321      * Query by a function. The specified function will be called with each
9322      * record in this data source. If the function returns true the record is included
9323      * in the results.
9324      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9325      * @param {Object} scope (optional) The scope of the function (defaults to this)
9326       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9327      **/
9328     queryBy : function(fn, scope){
9329         var data = this.snapshot || this.data;
9330         return data.filterBy(fn, scope||this);
9331     },
9332
9333     /**
9334      * Collects unique values for a particular dataIndex from this store.
9335      * @param {String} dataIndex The property to collect
9336      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
9337      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
9338      * @return {Array} An array of the unique values
9339      **/
9340     collect : function(dataIndex, allowNull, bypassFilter){
9341         var d = (bypassFilter === true && this.snapshot) ?
9342                 this.snapshot.items : this.data.items;
9343         var v, sv, r = [], l = {};
9344         for(var i = 0, len = d.length; i < len; i++){
9345             v = d[i].data[dataIndex];
9346             sv = String(v);
9347             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
9348                 l[sv] = true;
9349                 r[r.length] = v;
9350             }
9351         }
9352         return r;
9353     },
9354
9355     /**
9356      * Revert to a view of the Record cache with no filtering applied.
9357      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
9358      */
9359     clearFilter : function(suppressEvent){
9360         if(this.snapshot && this.snapshot != this.data){
9361             this.data = this.snapshot;
9362             delete this.snapshot;
9363             if(suppressEvent !== true){
9364                 this.fireEvent("datachanged", this);
9365             }
9366         }
9367     },
9368
9369     // private
9370     afterEdit : function(record){
9371         if(this.modified.indexOf(record) == -1){
9372             this.modified.push(record);
9373         }
9374         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
9375     },
9376     
9377     // private
9378     afterReject : function(record){
9379         this.modified.remove(record);
9380         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
9381     },
9382
9383     // private
9384     afterCommit : function(record){
9385         this.modified.remove(record);
9386         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
9387     },
9388
9389     /**
9390      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
9391      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
9392      */
9393     commitChanges : function(){
9394         var m = this.modified.slice(0);
9395         this.modified = [];
9396         for(var i = 0, len = m.length; i < len; i++){
9397             m[i].commit();
9398         }
9399     },
9400
9401     /**
9402      * Cancel outstanding changes on all changed records.
9403      */
9404     rejectChanges : function(){
9405         var m = this.modified.slice(0);
9406         this.modified = [];
9407         for(var i = 0, len = m.length; i < len; i++){
9408             m[i].reject();
9409         }
9410     },
9411
9412     onMetaChange : function(meta, rtype, o){
9413         this.recordType = rtype;
9414         this.fields = rtype.prototype.fields;
9415         delete this.snapshot;
9416         this.sortInfo = meta.sortInfo || this.sortInfo;
9417         this.modified = [];
9418         this.fireEvent('metachange', this, this.reader.meta);
9419     },
9420     
9421     moveIndex : function(data, type)
9422     {
9423         var index = this.indexOf(data);
9424         
9425         var newIndex = index + type;
9426         
9427         this.remove(data);
9428         
9429         this.insert(newIndex, data);
9430         
9431     }
9432 });/*
9433  * Based on:
9434  * Ext JS Library 1.1.1
9435  * Copyright(c) 2006-2007, Ext JS, LLC.
9436  *
9437  * Originally Released Under LGPL - original licence link has changed is not relivant.
9438  *
9439  * Fork - LGPL
9440  * <script type="text/javascript">
9441  */
9442
9443 /**
9444  * @class Roo.data.SimpleStore
9445  * @extends Roo.data.Store
9446  * Small helper class to make creating Stores from Array data easier.
9447  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
9448  * @cfg {Array} fields An array of field definition objects, or field name strings.
9449  * @cfg {Array} data The multi-dimensional array of data
9450  * @constructor
9451  * @param {Object} config
9452  */
9453 Roo.data.SimpleStore = function(config){
9454     Roo.data.SimpleStore.superclass.constructor.call(this, {
9455         isLocal : true,
9456         reader: new Roo.data.ArrayReader({
9457                 id: config.id
9458             },
9459             Roo.data.Record.create(config.fields)
9460         ),
9461         proxy : new Roo.data.MemoryProxy(config.data)
9462     });
9463     this.load();
9464 };
9465 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
9466  * Based on:
9467  * Ext JS Library 1.1.1
9468  * Copyright(c) 2006-2007, Ext JS, LLC.
9469  *
9470  * Originally Released Under LGPL - original licence link has changed is not relivant.
9471  *
9472  * Fork - LGPL
9473  * <script type="text/javascript">
9474  */
9475
9476 /**
9477 /**
9478  * @extends Roo.data.Store
9479  * @class Roo.data.JsonStore
9480  * Small helper class to make creating Stores for JSON data easier. <br/>
9481 <pre><code>
9482 var store = new Roo.data.JsonStore({
9483     url: 'get-images.php',
9484     root: 'images',
9485     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
9486 });
9487 </code></pre>
9488  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
9489  * JsonReader and HttpProxy (unless inline data is provided).</b>
9490  * @cfg {Array} fields An array of field definition objects, or field name strings.
9491  * @constructor
9492  * @param {Object} config
9493  */
9494 Roo.data.JsonStore = function(c){
9495     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
9496         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
9497         reader: new Roo.data.JsonReader(c, c.fields)
9498     }));
9499 };
9500 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
9501  * Based on:
9502  * Ext JS Library 1.1.1
9503  * Copyright(c) 2006-2007, Ext JS, LLC.
9504  *
9505  * Originally Released Under LGPL - original licence link has changed is not relivant.
9506  *
9507  * Fork - LGPL
9508  * <script type="text/javascript">
9509  */
9510
9511  
9512 Roo.data.Field = function(config){
9513     if(typeof config == "string"){
9514         config = {name: config};
9515     }
9516     Roo.apply(this, config);
9517     
9518     if(!this.type){
9519         this.type = "auto";
9520     }
9521     
9522     var st = Roo.data.SortTypes;
9523     // named sortTypes are supported, here we look them up
9524     if(typeof this.sortType == "string"){
9525         this.sortType = st[this.sortType];
9526     }
9527     
9528     // set default sortType for strings and dates
9529     if(!this.sortType){
9530         switch(this.type){
9531             case "string":
9532                 this.sortType = st.asUCString;
9533                 break;
9534             case "date":
9535                 this.sortType = st.asDate;
9536                 break;
9537             default:
9538                 this.sortType = st.none;
9539         }
9540     }
9541
9542     // define once
9543     var stripRe = /[\$,%]/g;
9544
9545     // prebuilt conversion function for this field, instead of
9546     // switching every time we're reading a value
9547     if(!this.convert){
9548         var cv, dateFormat = this.dateFormat;
9549         switch(this.type){
9550             case "":
9551             case "auto":
9552             case undefined:
9553                 cv = function(v){ return v; };
9554                 break;
9555             case "string":
9556                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
9557                 break;
9558             case "int":
9559                 cv = function(v){
9560                     return v !== undefined && v !== null && v !== '' ?
9561                            parseInt(String(v).replace(stripRe, ""), 10) : '';
9562                     };
9563                 break;
9564             case "float":
9565                 cv = function(v){
9566                     return v !== undefined && v !== null && v !== '' ?
9567                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
9568                     };
9569                 break;
9570             case "bool":
9571             case "boolean":
9572                 cv = function(v){ return v === true || v === "true" || v == 1; };
9573                 break;
9574             case "date":
9575                 cv = function(v){
9576                     if(!v){
9577                         return '';
9578                     }
9579                     if(v instanceof Date){
9580                         return v;
9581                     }
9582                     if(dateFormat){
9583                         if(dateFormat == "timestamp"){
9584                             return new Date(v*1000);
9585                         }
9586                         return Date.parseDate(v, dateFormat);
9587                     }
9588                     var parsed = Date.parse(v);
9589                     return parsed ? new Date(parsed) : null;
9590                 };
9591              break;
9592             
9593         }
9594         this.convert = cv;
9595     }
9596 };
9597
9598 Roo.data.Field.prototype = {
9599     dateFormat: null,
9600     defaultValue: "",
9601     mapping: null,
9602     sortType : null,
9603     sortDir : "ASC"
9604 };/*
9605  * Based on:
9606  * Ext JS Library 1.1.1
9607  * Copyright(c) 2006-2007, Ext JS, LLC.
9608  *
9609  * Originally Released Under LGPL - original licence link has changed is not relivant.
9610  *
9611  * Fork - LGPL
9612  * <script type="text/javascript">
9613  */
9614  
9615 // Base class for reading structured data from a data source.  This class is intended to be
9616 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
9617
9618 /**
9619  * @class Roo.data.DataReader
9620  * Base class for reading structured data from a data source.  This class is intended to be
9621  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
9622  */
9623
9624 Roo.data.DataReader = function(meta, recordType){
9625     
9626     this.meta = meta;
9627     
9628     this.recordType = recordType instanceof Array ? 
9629         Roo.data.Record.create(recordType) : recordType;
9630 };
9631
9632 Roo.data.DataReader.prototype = {
9633      /**
9634      * Create an empty record
9635      * @param {Object} data (optional) - overlay some values
9636      * @return {Roo.data.Record} record created.
9637      */
9638     newRow :  function(d) {
9639         var da =  {};
9640         this.recordType.prototype.fields.each(function(c) {
9641             switch( c.type) {
9642                 case 'int' : da[c.name] = 0; break;
9643                 case 'date' : da[c.name] = new Date(); break;
9644                 case 'float' : da[c.name] = 0.0; break;
9645                 case 'boolean' : da[c.name] = false; break;
9646                 default : da[c.name] = ""; break;
9647             }
9648             
9649         });
9650         return new this.recordType(Roo.apply(da, d));
9651     }
9652     
9653 };/*
9654  * Based on:
9655  * Ext JS Library 1.1.1
9656  * Copyright(c) 2006-2007, Ext JS, LLC.
9657  *
9658  * Originally Released Under LGPL - original licence link has changed is not relivant.
9659  *
9660  * Fork - LGPL
9661  * <script type="text/javascript">
9662  */
9663
9664 /**
9665  * @class Roo.data.DataProxy
9666  * @extends Roo.data.Observable
9667  * This class is an abstract base class for implementations which provide retrieval of
9668  * unformatted data objects.<br>
9669  * <p>
9670  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
9671  * (of the appropriate type which knows how to parse the data object) to provide a block of
9672  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
9673  * <p>
9674  * Custom implementations must implement the load method as described in
9675  * {@link Roo.data.HttpProxy#load}.
9676  */
9677 Roo.data.DataProxy = function(){
9678     this.addEvents({
9679         /**
9680          * @event beforeload
9681          * Fires before a network request is made to retrieve a data object.
9682          * @param {Object} This DataProxy object.
9683          * @param {Object} params The params parameter to the load function.
9684          */
9685         beforeload : true,
9686         /**
9687          * @event load
9688          * Fires before the load method's callback is called.
9689          * @param {Object} This DataProxy object.
9690          * @param {Object} o The data object.
9691          * @param {Object} arg The callback argument object passed to the load function.
9692          */
9693         load : true,
9694         /**
9695          * @event loadexception
9696          * Fires if an Exception occurs during data retrieval.
9697          * @param {Object} This DataProxy object.
9698          * @param {Object} o The data object.
9699          * @param {Object} arg The callback argument object passed to the load function.
9700          * @param {Object} e The Exception.
9701          */
9702         loadexception : true
9703     });
9704     Roo.data.DataProxy.superclass.constructor.call(this);
9705 };
9706
9707 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
9708
9709     /**
9710      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
9711      */
9712 /*
9713  * Based on:
9714  * Ext JS Library 1.1.1
9715  * Copyright(c) 2006-2007, Ext JS, LLC.
9716  *
9717  * Originally Released Under LGPL - original licence link has changed is not relivant.
9718  *
9719  * Fork - LGPL
9720  * <script type="text/javascript">
9721  */
9722 /**
9723  * @class Roo.data.MemoryProxy
9724  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
9725  * to the Reader when its load method is called.
9726  * @constructor
9727  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
9728  */
9729 Roo.data.MemoryProxy = function(data){
9730     if (data.data) {
9731         data = data.data;
9732     }
9733     Roo.data.MemoryProxy.superclass.constructor.call(this);
9734     this.data = data;
9735 };
9736
9737 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
9738     /**
9739      * Load data from the requested source (in this case an in-memory
9740      * data object passed to the constructor), read the data object into
9741      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9742      * process that block using the passed callback.
9743      * @param {Object} params This parameter is not used by the MemoryProxy class.
9744      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9745      * object into a block of Roo.data.Records.
9746      * @param {Function} callback The function into which to pass the block of Roo.data.records.
9747      * The function must be passed <ul>
9748      * <li>The Record block object</li>
9749      * <li>The "arg" argument from the load function</li>
9750      * <li>A boolean success indicator</li>
9751      * </ul>
9752      * @param {Object} scope The scope in which to call the callback
9753      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9754      */
9755     load : function(params, reader, callback, scope, arg){
9756         params = params || {};
9757         var result;
9758         try {
9759             result = reader.readRecords(this.data);
9760         }catch(e){
9761             this.fireEvent("loadexception", this, arg, null, e);
9762             callback.call(scope, null, arg, false);
9763             return;
9764         }
9765         callback.call(scope, result, arg, true);
9766     },
9767     
9768     // private
9769     update : function(params, records){
9770         
9771     }
9772 });/*
9773  * Based on:
9774  * Ext JS Library 1.1.1
9775  * Copyright(c) 2006-2007, Ext JS, LLC.
9776  *
9777  * Originally Released Under LGPL - original licence link has changed is not relivant.
9778  *
9779  * Fork - LGPL
9780  * <script type="text/javascript">
9781  */
9782 /**
9783  * @class Roo.data.HttpProxy
9784  * @extends Roo.data.DataProxy
9785  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
9786  * configured to reference a certain URL.<br><br>
9787  * <p>
9788  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
9789  * from which the running page was served.<br><br>
9790  * <p>
9791  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
9792  * <p>
9793  * Be aware that to enable the browser to parse an XML document, the server must set
9794  * the Content-Type header in the HTTP response to "text/xml".
9795  * @constructor
9796  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
9797  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
9798  * will be used to make the request.
9799  */
9800 Roo.data.HttpProxy = function(conn){
9801     Roo.data.HttpProxy.superclass.constructor.call(this);
9802     // is conn a conn config or a real conn?
9803     this.conn = conn;
9804     this.useAjax = !conn || !conn.events;
9805   
9806 };
9807
9808 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
9809     // thse are take from connection...
9810     
9811     /**
9812      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
9813      */
9814     /**
9815      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
9816      * extra parameters to each request made by this object. (defaults to undefined)
9817      */
9818     /**
9819      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
9820      *  to each request made by this object. (defaults to undefined)
9821      */
9822     /**
9823      * @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)
9824      */
9825     /**
9826      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
9827      */
9828      /**
9829      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
9830      * @type Boolean
9831      */
9832   
9833
9834     /**
9835      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
9836      * @type Boolean
9837      */
9838     /**
9839      * Return the {@link Roo.data.Connection} object being used by this Proxy.
9840      * @return {Connection} The Connection object. This object may be used to subscribe to events on
9841      * a finer-grained basis than the DataProxy events.
9842      */
9843     getConnection : function(){
9844         return this.useAjax ? Roo.Ajax : this.conn;
9845     },
9846
9847     /**
9848      * Load data from the configured {@link Roo.data.Connection}, read the data object into
9849      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
9850      * process that block using the passed callback.
9851      * @param {Object} params An object containing properties which are to be used as HTTP parameters
9852      * for the request to the remote server.
9853      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9854      * object into a block of Roo.data.Records.
9855      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9856      * The function must be passed <ul>
9857      * <li>The Record block object</li>
9858      * <li>The "arg" argument from the load function</li>
9859      * <li>A boolean success indicator</li>
9860      * </ul>
9861      * @param {Object} scope The scope in which to call the callback
9862      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9863      */
9864     load : function(params, reader, callback, scope, arg){
9865         if(this.fireEvent("beforeload", this, params) !== false){
9866             var  o = {
9867                 params : params || {},
9868                 request: {
9869                     callback : callback,
9870                     scope : scope,
9871                     arg : arg
9872                 },
9873                 reader: reader,
9874                 callback : this.loadResponse,
9875                 scope: this
9876             };
9877             if(this.useAjax){
9878                 Roo.applyIf(o, this.conn);
9879                 if(this.activeRequest){
9880                     Roo.Ajax.abort(this.activeRequest);
9881                 }
9882                 this.activeRequest = Roo.Ajax.request(o);
9883             }else{
9884                 this.conn.request(o);
9885             }
9886         }else{
9887             callback.call(scope||this, null, arg, false);
9888         }
9889     },
9890
9891     // private
9892     loadResponse : function(o, success, response){
9893         delete this.activeRequest;
9894         if(!success){
9895             this.fireEvent("loadexception", this, o, response);
9896             o.request.callback.call(o.request.scope, null, o.request.arg, false);
9897             return;
9898         }
9899         var result;
9900         try {
9901             result = o.reader.read(response);
9902         }catch(e){
9903             this.fireEvent("loadexception", this, o, response, e);
9904             o.request.callback.call(o.request.scope, null, o.request.arg, false);
9905             return;
9906         }
9907         
9908         this.fireEvent("load", this, o, o.request.arg);
9909         o.request.callback.call(o.request.scope, result, o.request.arg, true);
9910     },
9911
9912     // private
9913     update : function(dataSet){
9914
9915     },
9916
9917     // private
9918     updateResponse : function(dataSet){
9919
9920     }
9921 });/*
9922  * Based on:
9923  * Ext JS Library 1.1.1
9924  * Copyright(c) 2006-2007, Ext JS, LLC.
9925  *
9926  * Originally Released Under LGPL - original licence link has changed is not relivant.
9927  *
9928  * Fork - LGPL
9929  * <script type="text/javascript">
9930  */
9931
9932 /**
9933  * @class Roo.data.ScriptTagProxy
9934  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
9935  * other than the originating domain of the running page.<br><br>
9936  * <p>
9937  * <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
9938  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
9939  * <p>
9940  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
9941  * source code that is used as the source inside a &lt;script> tag.<br><br>
9942  * <p>
9943  * In order for the browser to process the returned data, the server must wrap the data object
9944  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
9945  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
9946  * depending on whether the callback name was passed:
9947  * <p>
9948  * <pre><code>
9949 boolean scriptTag = false;
9950 String cb = request.getParameter("callback");
9951 if (cb != null) {
9952     scriptTag = true;
9953     response.setContentType("text/javascript");
9954 } else {
9955     response.setContentType("application/x-json");
9956 }
9957 Writer out = response.getWriter();
9958 if (scriptTag) {
9959     out.write(cb + "(");
9960 }
9961 out.print(dataBlock.toJsonString());
9962 if (scriptTag) {
9963     out.write(");");
9964 }
9965 </pre></code>
9966  *
9967  * @constructor
9968  * @param {Object} config A configuration object.
9969  */
9970 Roo.data.ScriptTagProxy = function(config){
9971     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
9972     Roo.apply(this, config);
9973     this.head = document.getElementsByTagName("head")[0];
9974 };
9975
9976 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
9977
9978 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
9979     /**
9980      * @cfg {String} url The URL from which to request the data object.
9981      */
9982     /**
9983      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
9984      */
9985     timeout : 30000,
9986     /**
9987      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
9988      * the server the name of the callback function set up by the load call to process the returned data object.
9989      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
9990      * javascript output which calls this named function passing the data object as its only parameter.
9991      */
9992     callbackParam : "callback",
9993     /**
9994      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
9995      * name to the request.
9996      */
9997     nocache : true,
9998
9999     /**
10000      * Load data from the configured URL, read the data object into
10001      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10002      * process that block using the passed callback.
10003      * @param {Object} params An object containing properties which are to be used as HTTP parameters
10004      * for the request to the remote server.
10005      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10006      * object into a block of Roo.data.Records.
10007      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10008      * The function must be passed <ul>
10009      * <li>The Record block object</li>
10010      * <li>The "arg" argument from the load function</li>
10011      * <li>A boolean success indicator</li>
10012      * </ul>
10013      * @param {Object} scope The scope in which to call the callback
10014      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10015      */
10016     load : function(params, reader, callback, scope, arg){
10017         if(this.fireEvent("beforeload", this, params) !== false){
10018
10019             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
10020
10021             var url = this.url;
10022             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
10023             if(this.nocache){
10024                 url += "&_dc=" + (new Date().getTime());
10025             }
10026             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
10027             var trans = {
10028                 id : transId,
10029                 cb : "stcCallback"+transId,
10030                 scriptId : "stcScript"+transId,
10031                 params : params,
10032                 arg : arg,
10033                 url : url,
10034                 callback : callback,
10035                 scope : scope,
10036                 reader : reader
10037             };
10038             var conn = this;
10039
10040             window[trans.cb] = function(o){
10041                 conn.handleResponse(o, trans);
10042             };
10043
10044             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
10045
10046             if(this.autoAbort !== false){
10047                 this.abort();
10048             }
10049
10050             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
10051
10052             var script = document.createElement("script");
10053             script.setAttribute("src", url);
10054             script.setAttribute("type", "text/javascript");
10055             script.setAttribute("id", trans.scriptId);
10056             this.head.appendChild(script);
10057
10058             this.trans = trans;
10059         }else{
10060             callback.call(scope||this, null, arg, false);
10061         }
10062     },
10063
10064     // private
10065     isLoading : function(){
10066         return this.trans ? true : false;
10067     },
10068
10069     /**
10070      * Abort the current server request.
10071      */
10072     abort : function(){
10073         if(this.isLoading()){
10074             this.destroyTrans(this.trans);
10075         }
10076     },
10077
10078     // private
10079     destroyTrans : function(trans, isLoaded){
10080         this.head.removeChild(document.getElementById(trans.scriptId));
10081         clearTimeout(trans.timeoutId);
10082         if(isLoaded){
10083             window[trans.cb] = undefined;
10084             try{
10085                 delete window[trans.cb];
10086             }catch(e){}
10087         }else{
10088             // if hasn't been loaded, wait for load to remove it to prevent script error
10089             window[trans.cb] = function(){
10090                 window[trans.cb] = undefined;
10091                 try{
10092                     delete window[trans.cb];
10093                 }catch(e){}
10094             };
10095         }
10096     },
10097
10098     // private
10099     handleResponse : function(o, trans){
10100         this.trans = false;
10101         this.destroyTrans(trans, true);
10102         var result;
10103         try {
10104             result = trans.reader.readRecords(o);
10105         }catch(e){
10106             this.fireEvent("loadexception", this, o, trans.arg, e);
10107             trans.callback.call(trans.scope||window, null, trans.arg, false);
10108             return;
10109         }
10110         this.fireEvent("load", this, o, trans.arg);
10111         trans.callback.call(trans.scope||window, result, trans.arg, true);
10112     },
10113
10114     // private
10115     handleFailure : function(trans){
10116         this.trans = false;
10117         this.destroyTrans(trans, false);
10118         this.fireEvent("loadexception", this, null, trans.arg);
10119         trans.callback.call(trans.scope||window, null, trans.arg, false);
10120     }
10121 });/*
10122  * Based on:
10123  * Ext JS Library 1.1.1
10124  * Copyright(c) 2006-2007, Ext JS, LLC.
10125  *
10126  * Originally Released Under LGPL - original licence link has changed is not relivant.
10127  *
10128  * Fork - LGPL
10129  * <script type="text/javascript">
10130  */
10131
10132 /**
10133  * @class Roo.data.JsonReader
10134  * @extends Roo.data.DataReader
10135  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
10136  * based on mappings in a provided Roo.data.Record constructor.
10137  * 
10138  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
10139  * in the reply previously. 
10140  * 
10141  * <p>
10142  * Example code:
10143  * <pre><code>
10144 var RecordDef = Roo.data.Record.create([
10145     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
10146     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
10147 ]);
10148 var myReader = new Roo.data.JsonReader({
10149     totalProperty: "results",    // The property which contains the total dataset size (optional)
10150     root: "rows",                // The property which contains an Array of row objects
10151     id: "id"                     // The property within each row object that provides an ID for the record (optional)
10152 }, RecordDef);
10153 </code></pre>
10154  * <p>
10155  * This would consume a JSON file like this:
10156  * <pre><code>
10157 { 'results': 2, 'rows': [
10158     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
10159     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
10160 }
10161 </code></pre>
10162  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
10163  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
10164  * paged from the remote server.
10165  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
10166  * @cfg {String} root name of the property which contains the Array of row objects.
10167  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
10168  * @constructor
10169  * Create a new JsonReader
10170  * @param {Object} meta Metadata configuration options
10171  * @param {Object} recordType Either an Array of field definition objects,
10172  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
10173  */
10174 Roo.data.JsonReader = function(meta, recordType){
10175     
10176     meta = meta || {};
10177     // set some defaults:
10178     Roo.applyIf(meta, {
10179         totalProperty: 'total',
10180         successProperty : 'success',
10181         root : 'data',
10182         id : 'id'
10183     });
10184     
10185     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
10186 };
10187 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
10188     
10189     /**
10190      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
10191      * Used by Store query builder to append _requestMeta to params.
10192      * 
10193      */
10194     metaFromRemote : false,
10195     /**
10196      * This method is only used by a DataProxy which has retrieved data from a remote server.
10197      * @param {Object} response The XHR object which contains the JSON data in its responseText.
10198      * @return {Object} data A data block which is used by an Roo.data.Store object as
10199      * a cache of Roo.data.Records.
10200      */
10201     read : function(response){
10202         var json = response.responseText;
10203        
10204         var o = /* eval:var:o */ eval("("+json+")");
10205         if(!o) {
10206             throw {message: "JsonReader.read: Json object not found"};
10207         }
10208         
10209         if(o.metaData){
10210             
10211             delete this.ef;
10212             this.metaFromRemote = true;
10213             this.meta = o.metaData;
10214             this.recordType = Roo.data.Record.create(o.metaData.fields);
10215             this.onMetaChange(this.meta, this.recordType, o);
10216         }
10217         return this.readRecords(o);
10218     },
10219
10220     // private function a store will implement
10221     onMetaChange : function(meta, recordType, o){
10222
10223     },
10224
10225     /**
10226          * @ignore
10227          */
10228     simpleAccess: function(obj, subsc) {
10229         return obj[subsc];
10230     },
10231
10232         /**
10233          * @ignore
10234          */
10235     getJsonAccessor: function(){
10236         var re = /[\[\.]/;
10237         return function(expr) {
10238             try {
10239                 return(re.test(expr))
10240                     ? new Function("obj", "return obj." + expr)
10241                     : function(obj){
10242                         return obj[expr];
10243                     };
10244             } catch(e){}
10245             return Roo.emptyFn;
10246         };
10247     }(),
10248
10249     /**
10250      * Create a data block containing Roo.data.Records from an XML document.
10251      * @param {Object} o An object which contains an Array of row objects in the property specified
10252      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
10253      * which contains the total size of the dataset.
10254      * @return {Object} data A data block which is used by an Roo.data.Store object as
10255      * a cache of Roo.data.Records.
10256      */
10257     readRecords : function(o){
10258         /**
10259          * After any data loads, the raw JSON data is available for further custom processing.
10260          * @type Object
10261          */
10262         this.o = o;
10263         var s = this.meta, Record = this.recordType,
10264             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
10265
10266 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
10267         if (!this.ef) {
10268             if(s.totalProperty) {
10269                     this.getTotal = this.getJsonAccessor(s.totalProperty);
10270                 }
10271                 if(s.successProperty) {
10272                     this.getSuccess = this.getJsonAccessor(s.successProperty);
10273                 }
10274                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
10275                 if (s.id) {
10276                         var g = this.getJsonAccessor(s.id);
10277                         this.getId = function(rec) {
10278                                 var r = g(rec);  
10279                                 return (r === undefined || r === "") ? null : r;
10280                         };
10281                 } else {
10282                         this.getId = function(){return null;};
10283                 }
10284             this.ef = [];
10285             for(var jj = 0; jj < fl; jj++){
10286                 f = fi[jj];
10287                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
10288                 this.ef[jj] = this.getJsonAccessor(map);
10289             }
10290         }
10291
10292         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
10293         if(s.totalProperty){
10294             var vt = parseInt(this.getTotal(o), 10);
10295             if(!isNaN(vt)){
10296                 totalRecords = vt;
10297             }
10298         }
10299         if(s.successProperty){
10300             var vs = this.getSuccess(o);
10301             if(vs === false || vs === 'false'){
10302                 success = false;
10303             }
10304         }
10305         var records = [];
10306         for(var i = 0; i < c; i++){
10307                 var n = root[i];
10308             var values = {};
10309             var id = this.getId(n);
10310             for(var j = 0; j < fl; j++){
10311                 f = fi[j];
10312             var v = this.ef[j](n);
10313             if (!f.convert) {
10314                 Roo.log('missing convert for ' + f.name);
10315                 Roo.log(f);
10316                 continue;
10317             }
10318             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
10319             }
10320             var record = new Record(values, id);
10321             record.json = n;
10322             records[i] = record;
10323         }
10324         return {
10325             raw : o,
10326             success : success,
10327             records : records,
10328             totalRecords : totalRecords
10329         };
10330     }
10331 });/*
10332  * Based on:
10333  * Ext JS Library 1.1.1
10334  * Copyright(c) 2006-2007, Ext JS, LLC.
10335  *
10336  * Originally Released Under LGPL - original licence link has changed is not relivant.
10337  *
10338  * Fork - LGPL
10339  * <script type="text/javascript">
10340  */
10341
10342 /**
10343  * @class Roo.data.ArrayReader
10344  * @extends Roo.data.DataReader
10345  * Data reader class to create an Array of Roo.data.Record objects from an Array.
10346  * Each element of that Array represents a row of data fields. The
10347  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
10348  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
10349  * <p>
10350  * Example code:.
10351  * <pre><code>
10352 var RecordDef = Roo.data.Record.create([
10353     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
10354     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
10355 ]);
10356 var myReader = new Roo.data.ArrayReader({
10357     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
10358 }, RecordDef);
10359 </code></pre>
10360  * <p>
10361  * This would consume an Array like this:
10362  * <pre><code>
10363 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
10364   </code></pre>
10365  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
10366  * @constructor
10367  * Create a new JsonReader
10368  * @param {Object} meta Metadata configuration options.
10369  * @param {Object} recordType Either an Array of field definition objects
10370  * as specified to {@link Roo.data.Record#create},
10371  * or an {@link Roo.data.Record} object
10372  * created using {@link Roo.data.Record#create}.
10373  */
10374 Roo.data.ArrayReader = function(meta, recordType){
10375     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
10376 };
10377
10378 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
10379     /**
10380      * Create a data block containing Roo.data.Records from an XML document.
10381      * @param {Object} o An Array of row objects which represents the dataset.
10382      * @return {Object} data A data block which is used by an Roo.data.Store object as
10383      * a cache of Roo.data.Records.
10384      */
10385     readRecords : function(o){
10386         var sid = this.meta ? this.meta.id : null;
10387         var recordType = this.recordType, fields = recordType.prototype.fields;
10388         var records = [];
10389         var root = o;
10390             for(var i = 0; i < root.length; i++){
10391                     var n = root[i];
10392                 var values = {};
10393                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
10394                 for(var j = 0, jlen = fields.length; j < jlen; j++){
10395                 var f = fields.items[j];
10396                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
10397                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
10398                 v = f.convert(v);
10399                 values[f.name] = v;
10400             }
10401                 var record = new recordType(values, id);
10402                 record.json = n;
10403                 records[records.length] = record;
10404             }
10405             return {
10406                 records : records,
10407                 totalRecords : records.length
10408             };
10409     }
10410 });/*
10411  * - LGPL
10412  * * 
10413  */
10414
10415 /**
10416  * @class Roo.bootstrap.ComboBox
10417  * @extends Roo.bootstrap.TriggerField
10418  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
10419  * @cfg {Boolean} append (true|false) default false
10420  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
10421  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
10422  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
10423  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
10424  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
10425  * @constructor
10426  * Create a new ComboBox.
10427  * @param {Object} config Configuration options
10428  */
10429 Roo.bootstrap.ComboBox = function(config){
10430     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
10431     this.addEvents({
10432         /**
10433          * @event expand
10434          * Fires when the dropdown list is expanded
10435              * @param {Roo.bootstrap.ComboBox} combo This combo box
10436              */
10437         'expand' : true,
10438         /**
10439          * @event collapse
10440          * Fires when the dropdown list is collapsed
10441              * @param {Roo.bootstrap.ComboBox} combo This combo box
10442              */
10443         'collapse' : true,
10444         /**
10445          * @event beforeselect
10446          * Fires before a list item is selected. Return false to cancel the selection.
10447              * @param {Roo.bootstrap.ComboBox} combo This combo box
10448              * @param {Roo.data.Record} record The data record returned from the underlying store
10449              * @param {Number} index The index of the selected item in the dropdown list
10450              */
10451         'beforeselect' : true,
10452         /**
10453          * @event select
10454          * Fires when a list item is selected
10455              * @param {Roo.bootstrap.ComboBox} combo This combo box
10456              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
10457              * @param {Number} index The index of the selected item in the dropdown list
10458              */
10459         'select' : true,
10460         /**
10461          * @event beforequery
10462          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
10463          * The event object passed has these properties:
10464              * @param {Roo.bootstrap.ComboBox} combo This combo box
10465              * @param {String} query The query
10466              * @param {Boolean} forceAll true to force "all" query
10467              * @param {Boolean} cancel true to cancel the query
10468              * @param {Object} e The query event object
10469              */
10470         'beforequery': true,
10471          /**
10472          * @event add
10473          * Fires when the 'add' icon is pressed (add a listener to enable add button)
10474              * @param {Roo.bootstrap.ComboBox} combo This combo box
10475              */
10476         'add' : true,
10477         /**
10478          * @event edit
10479          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
10480              * @param {Roo.bootstrap.ComboBox} combo This combo box
10481              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
10482              */
10483         'edit' : true,
10484         /**
10485          * @event remove
10486          * Fires when the remove value from the combobox array
10487              * @param {Roo.bootstrap.ComboBox} combo This combo box
10488              */
10489         'remove' : true
10490         
10491     });
10492     
10493     this.item = [];
10494     this.tickItems = [];
10495     
10496     this.selectedIndex = -1;
10497     if(this.mode == 'local'){
10498         if(config.queryDelay === undefined){
10499             this.queryDelay = 10;
10500         }
10501         if(config.minChars === undefined){
10502             this.minChars = 0;
10503         }
10504     }
10505 };
10506
10507 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
10508      
10509     /**
10510      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
10511      * rendering into an Roo.Editor, defaults to false)
10512      */
10513     /**
10514      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
10515      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
10516      */
10517     /**
10518      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
10519      */
10520     /**
10521      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
10522      * the dropdown list (defaults to undefined, with no header element)
10523      */
10524
10525      /**
10526      * @cfg {String/Roo.Template} tpl The template to use to render the output
10527      */
10528      
10529      /**
10530      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
10531      */
10532     listWidth: undefined,
10533     /**
10534      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
10535      * mode = 'remote' or 'text' if mode = 'local')
10536      */
10537     displayField: undefined,
10538     /**
10539      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
10540      * mode = 'remote' or 'value' if mode = 'local'). 
10541      * Note: use of a valueField requires the user make a selection
10542      * in order for a value to be mapped.
10543      */
10544     valueField: undefined,
10545     
10546     
10547     /**
10548      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
10549      * field's data value (defaults to the underlying DOM element's name)
10550      */
10551     hiddenName: undefined,
10552     /**
10553      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
10554      */
10555     listClass: '',
10556     /**
10557      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
10558      */
10559     selectedClass: 'active',
10560     
10561     /**
10562      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
10563      */
10564     shadow:'sides',
10565     /**
10566      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
10567      * anchor positions (defaults to 'tl-bl')
10568      */
10569     listAlign: 'tl-bl?',
10570     /**
10571      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
10572      */
10573     maxHeight: 300,
10574     /**
10575      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
10576      * query specified by the allQuery config option (defaults to 'query')
10577      */
10578     triggerAction: 'query',
10579     /**
10580      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
10581      * (defaults to 4, does not apply if editable = false)
10582      */
10583     minChars : 4,
10584     /**
10585      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
10586      * delay (typeAheadDelay) if it matches a known value (defaults to false)
10587      */
10588     typeAhead: false,
10589     /**
10590      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
10591      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
10592      */
10593     queryDelay: 500,
10594     /**
10595      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
10596      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
10597      */
10598     pageSize: 0,
10599     /**
10600      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
10601      * when editable = true (defaults to false)
10602      */
10603     selectOnFocus:false,
10604     /**
10605      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
10606      */
10607     queryParam: 'query',
10608     /**
10609      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
10610      * when mode = 'remote' (defaults to 'Loading...')
10611      */
10612     loadingText: 'Loading...',
10613     /**
10614      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
10615      */
10616     resizable: false,
10617     /**
10618      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
10619      */
10620     handleHeight : 8,
10621     /**
10622      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
10623      * traditional select (defaults to true)
10624      */
10625     editable: true,
10626     /**
10627      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
10628      */
10629     allQuery: '',
10630     /**
10631      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
10632      */
10633     mode: 'remote',
10634     /**
10635      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
10636      * listWidth has a higher value)
10637      */
10638     minListWidth : 70,
10639     /**
10640      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
10641      * allow the user to set arbitrary text into the field (defaults to false)
10642      */
10643     forceSelection:false,
10644     /**
10645      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
10646      * if typeAhead = true (defaults to 250)
10647      */
10648     typeAheadDelay : 250,
10649     /**
10650      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
10651      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
10652      */
10653     valueNotFoundText : undefined,
10654     /**
10655      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
10656      */
10657     blockFocus : false,
10658     
10659     /**
10660      * @cfg {Boolean} disableClear Disable showing of clear button.
10661      */
10662     disableClear : false,
10663     /**
10664      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
10665      */
10666     alwaysQuery : false,
10667     
10668     /**
10669      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
10670      */
10671     multiple : false,
10672     
10673     //private
10674     addicon : false,
10675     editicon: false,
10676     
10677     page: 0,
10678     hasQuery: false,
10679     append: false,
10680     loadNext: false,
10681     autoFocus : true,
10682     tickable : false,
10683     btnPosition : 'right',
10684     triggerList : true,
10685     showToggleBtn : true,
10686     // element that contains real text value.. (when hidden is used..)
10687     
10688     getAutoCreate : function()
10689     {
10690         var cfg = false;
10691         
10692         /*
10693          *  Normal ComboBox
10694          */
10695         if(!this.tickable){
10696             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
10697             return cfg;
10698         }
10699         
10700         /*
10701          *  ComboBox with tickable selections
10702          */
10703              
10704         var align = this.labelAlign || this.parentLabelAlign();
10705         
10706         cfg = {
10707             cls : 'form-group roo-combobox-tickable' //input-group
10708         };
10709         
10710         
10711         var buttons = {
10712             tag : 'div',
10713             cls : 'tickable-buttons',
10714             cn : [
10715                 {
10716                     tag : 'button',
10717                     type : 'button',
10718                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
10719                     html : 'Edit'
10720                 },
10721                 {
10722                     tag : 'button',
10723                     type : 'button',
10724                     name : 'ok',
10725                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
10726                     html : 'Done'
10727                 },
10728                 {
10729                     tag : 'button',
10730                     type : 'button',
10731                     name : 'cancel',
10732                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
10733                     html : 'Cancel'
10734                 }
10735             ]
10736         };
10737         
10738         var _this = this;
10739         Roo.each(buttons.cn, function(c){
10740             if (_this.size) {
10741                 c.cls += ' btn-' + _this.size;
10742             }
10743
10744             if (_this.disabled) {
10745                 c.disabled = true;
10746             }
10747         });
10748         
10749         var box = {
10750             tag: 'div',
10751             cn: [
10752                 {
10753                     tag: 'input',
10754                     type : 'hidden',
10755                     cls: 'form-hidden-field'
10756                 },
10757                 {
10758                     tag: 'ul',
10759                     cls: 'select2-choices',
10760                     cn:[
10761                         {
10762                             tag: 'li',
10763                             cls: 'select2-search-field',
10764                             cn: [
10765
10766                                 buttons
10767                             ]
10768                         }
10769                     ]
10770                 }
10771             ]
10772         }
10773         
10774         var combobox = {
10775             cls: 'select2-container input-group select2-container-multi',
10776             cn: [
10777                 box
10778 //                {
10779 //                    tag: 'ul',
10780 //                    cls: 'typeahead typeahead-long dropdown-menu',
10781 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
10782 //                }
10783             ]
10784         };
10785         
10786         if (align ==='left' && this.fieldLabel.length) {
10787             
10788                 Roo.log("left and has label");
10789                 cfg.cn = [
10790                     
10791                     {
10792                         tag: 'label',
10793                         'for' :  id,
10794                         cls : 'control-label col-sm-' + this.labelWidth,
10795                         html : this.fieldLabel
10796                         
10797                     },
10798                     {
10799                         cls : "col-sm-" + (12 - this.labelWidth), 
10800                         cn: [
10801                             combobox
10802                         ]
10803                     }
10804                     
10805                 ];
10806         } else if ( this.fieldLabel.length) {
10807                 Roo.log(" label");
10808                  cfg.cn = [
10809                    
10810                     {
10811                         tag: 'label',
10812                         //cls : 'input-group-addon',
10813                         html : this.fieldLabel
10814                         
10815                     },
10816                     
10817                     combobox
10818                     
10819                 ];
10820
10821         } else {
10822             
10823                 Roo.log(" no label && no align");
10824                 cfg = combobox
10825                      
10826                 
10827         }
10828          
10829         var settings=this;
10830         ['xs','sm','md','lg'].map(function(size){
10831             if (settings[size]) {
10832                 cfg.cls += ' col-' + size + '-' + settings[size];
10833             }
10834         });
10835         
10836         return cfg;
10837         
10838     },
10839     
10840     // private
10841     initEvents: function()
10842     {
10843         
10844         if (!this.store) {
10845             throw "can not find store for combo";
10846         }
10847         this.store = Roo.factory(this.store, Roo.data);
10848         
10849         if(this.tickable){
10850             this.initTickableEvents();
10851             return;
10852         }
10853         
10854         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
10855         
10856         if(this.hiddenName){
10857             
10858             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10859             
10860             this.hiddenField.dom.value =
10861                 this.hiddenValue !== undefined ? this.hiddenValue :
10862                 this.value !== undefined ? this.value : '';
10863
10864             // prevent input submission
10865             this.el.dom.removeAttribute('name');
10866             this.hiddenField.dom.setAttribute('name', this.hiddenName);
10867              
10868              
10869         }
10870         //if(Roo.isGecko){
10871         //    this.el.dom.setAttribute('autocomplete', 'off');
10872         //}
10873         
10874         var cls = 'x-combo-list';
10875         
10876         //this.list = new Roo.Layer({
10877         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
10878         //});
10879         
10880         var _this = this;
10881         
10882         (function(){
10883             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
10884             _this.list.setWidth(lw);
10885         }).defer(100);
10886         
10887         this.list.on('mouseover', this.onViewOver, this);
10888         this.list.on('mousemove', this.onViewMove, this);
10889         
10890         this.list.on('scroll', this.onViewScroll, this);
10891         
10892         /*
10893         this.list.swallowEvent('mousewheel');
10894         this.assetHeight = 0;
10895
10896         if(this.title){
10897             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
10898             this.assetHeight += this.header.getHeight();
10899         }
10900
10901         this.innerList = this.list.createChild({cls:cls+'-inner'});
10902         this.innerList.on('mouseover', this.onViewOver, this);
10903         this.innerList.on('mousemove', this.onViewMove, this);
10904         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
10905         
10906         if(this.allowBlank && !this.pageSize && !this.disableClear){
10907             this.footer = this.list.createChild({cls:cls+'-ft'});
10908             this.pageTb = new Roo.Toolbar(this.footer);
10909            
10910         }
10911         if(this.pageSize){
10912             this.footer = this.list.createChild({cls:cls+'-ft'});
10913             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
10914                     {pageSize: this.pageSize});
10915             
10916         }
10917         
10918         if (this.pageTb && this.allowBlank && !this.disableClear) {
10919             var _this = this;
10920             this.pageTb.add(new Roo.Toolbar.Fill(), {
10921                 cls: 'x-btn-icon x-btn-clear',
10922                 text: '&#160;',
10923                 handler: function()
10924                 {
10925                     _this.collapse();
10926                     _this.clearValue();
10927                     _this.onSelect(false, -1);
10928                 }
10929             });
10930         }
10931         if (this.footer) {
10932             this.assetHeight += this.footer.getHeight();
10933         }
10934         */
10935             
10936         if(!this.tpl){
10937             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
10938         }
10939
10940         this.view = new Roo.View(this.list, this.tpl, {
10941             singleSelect:true, store: this.store, selectedClass: this.selectedClass
10942         });
10943         //this.view.wrapEl.setDisplayed(false);
10944         this.view.on('click', this.onViewClick, this);
10945         
10946         
10947         
10948         this.store.on('beforeload', this.onBeforeLoad, this);
10949         this.store.on('load', this.onLoad, this);
10950         this.store.on('loadexception', this.onLoadException, this);
10951         /*
10952         if(this.resizable){
10953             this.resizer = new Roo.Resizable(this.list,  {
10954                pinned:true, handles:'se'
10955             });
10956             this.resizer.on('resize', function(r, w, h){
10957                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
10958                 this.listWidth = w;
10959                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
10960                 this.restrictHeight();
10961             }, this);
10962             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
10963         }
10964         */
10965         if(!this.editable){
10966             this.editable = true;
10967             this.setEditable(false);
10968         }
10969         
10970         /*
10971         
10972         if (typeof(this.events.add.listeners) != 'undefined') {
10973             
10974             this.addicon = this.wrap.createChild(
10975                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
10976        
10977             this.addicon.on('click', function(e) {
10978                 this.fireEvent('add', this);
10979             }, this);
10980         }
10981         if (typeof(this.events.edit.listeners) != 'undefined') {
10982             
10983             this.editicon = this.wrap.createChild(
10984                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
10985             if (this.addicon) {
10986                 this.editicon.setStyle('margin-left', '40px');
10987             }
10988             this.editicon.on('click', function(e) {
10989                 
10990                 // we fire even  if inothing is selected..
10991                 this.fireEvent('edit', this, this.lastData );
10992                 
10993             }, this);
10994         }
10995         */
10996         
10997         this.keyNav = new Roo.KeyNav(this.inputEl(), {
10998             "up" : function(e){
10999                 this.inKeyMode = true;
11000                 this.selectPrev();
11001             },
11002
11003             "down" : function(e){
11004                 if(!this.isExpanded()){
11005                     this.onTriggerClick();
11006                 }else{
11007                     this.inKeyMode = true;
11008                     this.selectNext();
11009                 }
11010             },
11011
11012             "enter" : function(e){
11013 //                this.onViewClick();
11014                 //return true;
11015                 this.collapse();
11016                 
11017                 if(this.fireEvent("specialkey", this, e)){
11018                     this.onViewClick(false);
11019                 }
11020                 
11021                 return true;
11022             },
11023
11024             "esc" : function(e){
11025                 this.collapse();
11026             },
11027
11028             "tab" : function(e){
11029                 this.collapse();
11030                 
11031                 if(this.fireEvent("specialkey", this, e)){
11032                     this.onViewClick(false);
11033                 }
11034                 
11035                 return true;
11036             },
11037
11038             scope : this,
11039
11040             doRelay : function(foo, bar, hname){
11041                 if(hname == 'down' || this.scope.isExpanded()){
11042                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11043                 }
11044                 return true;
11045             },
11046
11047             forceKeyDown: true
11048         });
11049         
11050         
11051         this.queryDelay = Math.max(this.queryDelay || 10,
11052                 this.mode == 'local' ? 10 : 250);
11053         
11054         
11055         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11056         
11057         if(this.typeAhead){
11058             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11059         }
11060         if(this.editable !== false){
11061             this.inputEl().on("keyup", this.onKeyUp, this);
11062         }
11063         if(this.forceSelection){
11064             this.inputEl().on('blur', this.doForce, this);
11065         }
11066         
11067         if(this.multiple){
11068             this.choices = this.el.select('ul.select2-choices', true).first();
11069             this.searchField = this.el.select('ul li.select2-search-field', true).first();
11070         }
11071     },
11072     
11073     initTickableEvents: function()
11074     {   
11075         this.createList();
11076         
11077         if(this.hiddenName){
11078             
11079             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11080             
11081             this.hiddenField.dom.value =
11082                 this.hiddenValue !== undefined ? this.hiddenValue :
11083                 this.value !== undefined ? this.value : '';
11084
11085             // prevent input submission
11086             this.el.dom.removeAttribute('name');
11087             this.hiddenField.dom.setAttribute('name', this.hiddenName);
11088              
11089              
11090         }
11091         
11092 //        this.list = this.el.select('ul.dropdown-menu',true).first();
11093         
11094         this.choices = this.el.select('ul.select2-choices', true).first();
11095         this.searchField = this.el.select('ul li.select2-search-field', true).first();
11096         if(this.triggerList){
11097             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
11098         }
11099          
11100         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
11101         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
11102         
11103         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
11104         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
11105         
11106         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
11107         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
11108         
11109         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
11110         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
11111         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
11112         
11113         this.okBtn.hide();
11114         this.cancelBtn.hide();
11115         
11116         var _this = this;
11117         
11118         (function(){
11119             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11120             _this.list.setWidth(lw);
11121         }).defer(100);
11122         
11123         this.list.on('mouseover', this.onViewOver, this);
11124         this.list.on('mousemove', this.onViewMove, this);
11125         
11126         this.list.on('scroll', this.onViewScroll, this);
11127         
11128         if(!this.tpl){
11129             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>';
11130         }
11131
11132         this.view = new Roo.View(this.list, this.tpl, {
11133             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
11134         });
11135         
11136         //this.view.wrapEl.setDisplayed(false);
11137         this.view.on('click', this.onViewClick, this);
11138         
11139         
11140         
11141         this.store.on('beforeload', this.onBeforeLoad, this);
11142         this.store.on('load', this.onLoad, this);
11143         this.store.on('loadexception', this.onLoadException, this);
11144         
11145 //        this.keyNav = new Roo.KeyNav(this.inputEl(), {
11146 //            "up" : function(e){
11147 //                this.inKeyMode = true;
11148 //                this.selectPrev();
11149 //            },
11150 //
11151 //            "down" : function(e){
11152 //                if(!this.isExpanded()){
11153 //                    this.onTriggerClick();
11154 //                }else{
11155 //                    this.inKeyMode = true;
11156 //                    this.selectNext();
11157 //                }
11158 //            },
11159 //
11160 //            "enter" : function(e){
11161 ////                this.onViewClick();
11162 //                //return true;
11163 //                this.collapse();
11164 //                
11165 //                if(this.fireEvent("specialkey", this, e)){
11166 //                    this.onViewClick(false);
11167 //                }
11168 //                
11169 //                return true;
11170 //            },
11171 //
11172 //            "esc" : function(e){
11173 //                this.collapse();
11174 //            },
11175 //
11176 //            "tab" : function(e){
11177 //                this.collapse();
11178 //                
11179 //                if(this.fireEvent("specialkey", this, e)){
11180 //                    this.onViewClick(false);
11181 //                }
11182 //                
11183 //                return true;
11184 //            },
11185 //
11186 //            scope : this,
11187 //
11188 //            doRelay : function(foo, bar, hname){
11189 //                if(hname == 'down' || this.scope.isExpanded()){
11190 //                   return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11191 //                }
11192 //                return true;
11193 //            },
11194 //
11195 //            forceKeyDown: true
11196 //        });
11197         
11198         
11199         this.queryDelay = Math.max(this.queryDelay || 10,
11200                 this.mode == 'local' ? 10 : 250);
11201         
11202         
11203         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11204         
11205         if(this.typeAhead){
11206             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11207         }
11208     },
11209
11210     onDestroy : function(){
11211         if(this.view){
11212             this.view.setStore(null);
11213             this.view.el.removeAllListeners();
11214             this.view.el.remove();
11215             this.view.purgeListeners();
11216         }
11217         if(this.list){
11218             this.list.dom.innerHTML  = '';
11219         }
11220         
11221         if(this.store){
11222             this.store.un('beforeload', this.onBeforeLoad, this);
11223             this.store.un('load', this.onLoad, this);
11224             this.store.un('loadexception', this.onLoadException, this);
11225         }
11226         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
11227     },
11228
11229     // private
11230     fireKey : function(e){
11231         if(e.isNavKeyPress() && !this.list.isVisible()){
11232             this.fireEvent("specialkey", this, e);
11233         }
11234     },
11235
11236     // private
11237     onResize: function(w, h){
11238 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
11239 //        
11240 //        if(typeof w != 'number'){
11241 //            // we do not handle it!?!?
11242 //            return;
11243 //        }
11244 //        var tw = this.trigger.getWidth();
11245 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
11246 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
11247 //        var x = w - tw;
11248 //        this.inputEl().setWidth( this.adjustWidth('input', x));
11249 //            
11250 //        //this.trigger.setStyle('left', x+'px');
11251 //        
11252 //        if(this.list && this.listWidth === undefined){
11253 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
11254 //            this.list.setWidth(lw);
11255 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11256 //        }
11257         
11258     
11259         
11260     },
11261
11262     /**
11263      * Allow or prevent the user from directly editing the field text.  If false is passed,
11264      * the user will only be able to select from the items defined in the dropdown list.  This method
11265      * is the runtime equivalent of setting the 'editable' config option at config time.
11266      * @param {Boolean} value True to allow the user to directly edit the field text
11267      */
11268     setEditable : function(value){
11269         if(value == this.editable){
11270             return;
11271         }
11272         this.editable = value;
11273         if(!value){
11274             this.inputEl().dom.setAttribute('readOnly', true);
11275             this.inputEl().on('mousedown', this.onTriggerClick,  this);
11276             this.inputEl().addClass('x-combo-noedit');
11277         }else{
11278             this.inputEl().dom.setAttribute('readOnly', false);
11279             this.inputEl().un('mousedown', this.onTriggerClick,  this);
11280             this.inputEl().removeClass('x-combo-noedit');
11281         }
11282     },
11283
11284     // private
11285     
11286     onBeforeLoad : function(combo,opts){
11287         if(!this.hasFocus){
11288             return;
11289         }
11290          if (!opts.add) {
11291             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
11292          }
11293         this.restrictHeight();
11294         this.selectedIndex = -1;
11295     },
11296
11297     // private
11298     onLoad : function(){
11299         
11300         this.hasQuery = false;
11301         
11302         if(!this.hasFocus){
11303             return;
11304         }
11305         
11306         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11307             this.loading.hide();
11308         }
11309         
11310         if(this.store.getCount() > 0){
11311             this.expand();
11312 //            this.restrictHeight();
11313             if(this.lastQuery == this.allQuery){
11314                 if(this.editable && !this.tickable){
11315                     this.inputEl().dom.select();
11316                 }
11317                 
11318                 if(
11319                     !this.selectByValue(this.value, true) &&
11320                     this.autoFocus && (typeof(this.store.lastOptions.add) == 'undefined' || 
11321                     this.store.lastOptions.add != true)
11322                 ){
11323                     this.select(0, true);
11324                 }
11325             }else{
11326                 if(this.autoFocus){
11327                     this.selectNext();
11328                 }
11329                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
11330                     this.taTask.delay(this.typeAheadDelay);
11331                 }
11332             }
11333         }else{
11334             this.onEmptyResults();
11335         }
11336         
11337         //this.el.focus();
11338     },
11339     // private
11340     onLoadException : function()
11341     {
11342         this.hasQuery = false;
11343         
11344         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11345             this.loading.hide();
11346         }
11347         
11348         this.collapse();
11349         Roo.log(this.store.reader.jsonData);
11350         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
11351             // fixme
11352             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
11353         }
11354         
11355         
11356     },
11357     // private
11358     onTypeAhead : function(){
11359         if(this.store.getCount() > 0){
11360             var r = this.store.getAt(0);
11361             var newValue = r.data[this.displayField];
11362             var len = newValue.length;
11363             var selStart = this.getRawValue().length;
11364             
11365             if(selStart != len){
11366                 this.setRawValue(newValue);
11367                 this.selectText(selStart, newValue.length);
11368             }
11369         }
11370     },
11371
11372     // private
11373     onSelect : function(record, index){
11374         
11375         if(this.fireEvent('beforeselect', this, record, index) !== false){
11376         
11377             this.setFromData(index > -1 ? record.data : false);
11378             
11379             this.collapse();
11380             this.fireEvent('select', this, record, index);
11381         }
11382     },
11383
11384     /**
11385      * Returns the currently selected field value or empty string if no value is set.
11386      * @return {String} value The selected value
11387      */
11388     getValue : function(){
11389         
11390         if(this.multiple){
11391             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
11392         }
11393         
11394         if(this.valueField){
11395             return typeof this.value != 'undefined' ? this.value : '';
11396         }else{
11397             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
11398         }
11399     },
11400
11401     /**
11402      * Clears any text/value currently set in the field
11403      */
11404     clearValue : function(){
11405         if(this.hiddenField){
11406             this.hiddenField.dom.value = '';
11407         }
11408         this.value = '';
11409         this.setRawValue('');
11410         this.lastSelectionText = '';
11411         
11412     },
11413
11414     /**
11415      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
11416      * will be displayed in the field.  If the value does not match the data value of an existing item,
11417      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
11418      * Otherwise the field will be blank (although the value will still be set).
11419      * @param {String} value The value to match
11420      */
11421     setValue : function(v){
11422         if(this.multiple){
11423             this.syncValue();
11424             return;
11425         }
11426         
11427         var text = v;
11428         if(this.valueField){
11429             var r = this.findRecord(this.valueField, v);
11430             if(r){
11431                 text = r.data[this.displayField];
11432             }else if(this.valueNotFoundText !== undefined){
11433                 text = this.valueNotFoundText;
11434             }
11435         }
11436         this.lastSelectionText = text;
11437         if(this.hiddenField){
11438             this.hiddenField.dom.value = v;
11439         }
11440         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
11441         this.value = v;
11442     },
11443     /**
11444      * @property {Object} the last set data for the element
11445      */
11446     
11447     lastData : false,
11448     /**
11449      * Sets the value of the field based on a object which is related to the record format for the store.
11450      * @param {Object} value the value to set as. or false on reset?
11451      */
11452     setFromData : function(o){
11453         
11454         if(this.multiple){
11455             this.addItem(o);
11456             return;
11457         }
11458             
11459         var dv = ''; // display value
11460         var vv = ''; // value value..
11461         this.lastData = o;
11462         if (this.displayField) {
11463             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11464         } else {
11465             // this is an error condition!!!
11466             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
11467         }
11468         
11469         if(this.valueField){
11470             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
11471         }
11472         
11473         if(this.hiddenField){
11474             this.hiddenField.dom.value = vv;
11475             
11476             this.lastSelectionText = dv;
11477             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11478             this.value = vv;
11479             return;
11480         }
11481         // no hidden field.. - we store the value in 'value', but still display
11482         // display field!!!!
11483         this.lastSelectionText = dv;
11484         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11485         this.value = vv;
11486         
11487         
11488     },
11489     // private
11490     reset : function(){
11491         // overridden so that last data is reset..
11492         this.setValue(this.originalValue);
11493         this.clearInvalid();
11494         this.lastData = false;
11495         if (this.view) {
11496             this.view.clearSelections();
11497         }
11498     },
11499     // private
11500     findRecord : function(prop, value){
11501         var record;
11502         if(this.store.getCount() > 0){
11503             this.store.each(function(r){
11504                 if(r.data[prop] == value){
11505                     record = r;
11506                     return false;
11507                 }
11508                 return true;
11509             });
11510         }
11511         return record;
11512     },
11513     
11514     getName: function()
11515     {
11516         // returns hidden if it's set..
11517         if (!this.rendered) {return ''};
11518         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
11519         
11520     },
11521     // private
11522     onViewMove : function(e, t){
11523         this.inKeyMode = false;
11524     },
11525
11526     // private
11527     onViewOver : function(e, t){
11528         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
11529             return;
11530         }
11531         var item = this.view.findItemFromChild(t);
11532         
11533         if(item){
11534             var index = this.view.indexOf(item);
11535             this.select(index, false);
11536         }
11537     },
11538
11539     // private
11540     onViewClick : function(view, doFocus, el, e)
11541     {
11542         var index = this.view.getSelectedIndexes()[0];
11543         
11544         var r = this.store.getAt(index);
11545         
11546         if(this.tickable){
11547             
11548             if(e.getTarget().nodeName.toLowerCase() != 'input'){
11549                 return;
11550             }
11551             
11552             var rm = false;
11553             var _this = this;
11554             
11555             Roo.each(this.tickItems, function(v,k){
11556                 
11557                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
11558                     _this.tickItems.splice(k, 1);
11559                     rm = true;
11560                     return;
11561                 }
11562             })
11563             
11564             if(rm){
11565                 return;
11566             }
11567             
11568             this.tickItems.push(r.data);
11569             return;
11570         }
11571         
11572         if(r){
11573             this.onSelect(r, index);
11574         }
11575         if(doFocus !== false && !this.blockFocus){
11576             this.inputEl().focus();
11577         }
11578     },
11579
11580     // private
11581     restrictHeight : function(){
11582         //this.innerList.dom.style.height = '';
11583         //var inner = this.innerList.dom;
11584         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
11585         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
11586         //this.list.beginUpdate();
11587         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
11588         this.list.alignTo(this.inputEl(), this.listAlign);
11589         this.list.alignTo(this.inputEl(), this.listAlign);
11590         //this.list.endUpdate();
11591     },
11592
11593     // private
11594     onEmptyResults : function(){
11595         this.collapse();
11596     },
11597
11598     /**
11599      * Returns true if the dropdown list is expanded, else false.
11600      */
11601     isExpanded : function(){
11602         return this.list.isVisible();
11603     },
11604
11605     /**
11606      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
11607      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11608      * @param {String} value The data value of the item to select
11609      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11610      * selected item if it is not currently in view (defaults to true)
11611      * @return {Boolean} True if the value matched an item in the list, else false
11612      */
11613     selectByValue : function(v, scrollIntoView){
11614         if(v !== undefined && v !== null){
11615             var r = this.findRecord(this.valueField || this.displayField, v);
11616             if(r){
11617                 this.select(this.store.indexOf(r), scrollIntoView);
11618                 return true;
11619             }
11620         }
11621         return false;
11622     },
11623
11624     /**
11625      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
11626      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11627      * @param {Number} index The zero-based index of the list item to select
11628      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11629      * selected item if it is not currently in view (defaults to true)
11630      */
11631     select : function(index, scrollIntoView){
11632         this.selectedIndex = index;
11633         this.view.select(index);
11634         if(scrollIntoView !== false){
11635             var el = this.view.getNode(index);
11636             if(el && !this.multiple && !this.tickable){
11637                 this.list.scrollChildIntoView(el, false);
11638             }
11639         }
11640     },
11641
11642     // private
11643     selectNext : function(){
11644         var ct = this.store.getCount();
11645         if(ct > 0){
11646             if(this.selectedIndex == -1){
11647                 this.select(0);
11648             }else if(this.selectedIndex < ct-1){
11649                 this.select(this.selectedIndex+1);
11650             }
11651         }
11652     },
11653
11654     // private
11655     selectPrev : function(){
11656         var ct = this.store.getCount();
11657         if(ct > 0){
11658             if(this.selectedIndex == -1){
11659                 this.select(0);
11660             }else if(this.selectedIndex != 0){
11661                 this.select(this.selectedIndex-1);
11662             }
11663         }
11664     },
11665
11666     // private
11667     onKeyUp : function(e){
11668         if(this.editable !== false && !e.isSpecialKey()){
11669             this.lastKey = e.getKey();
11670             this.dqTask.delay(this.queryDelay);
11671         }
11672     },
11673
11674     // private
11675     validateBlur : function(){
11676         return !this.list || !this.list.isVisible();   
11677     },
11678
11679     // private
11680     initQuery : function(){
11681         this.doQuery(this.getRawValue());
11682     },
11683
11684     // private
11685     doForce : function(){
11686         if(this.inputEl().dom.value.length > 0){
11687             this.inputEl().dom.value =
11688                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
11689              
11690         }
11691     },
11692
11693     /**
11694      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
11695      * query allowing the query action to be canceled if needed.
11696      * @param {String} query The SQL query to execute
11697      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
11698      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
11699      * saved in the current store (defaults to false)
11700      */
11701     doQuery : function(q, forceAll){
11702         
11703         if(q === undefined || q === null){
11704             q = '';
11705         }
11706         var qe = {
11707             query: q,
11708             forceAll: forceAll,
11709             combo: this,
11710             cancel:false
11711         };
11712         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
11713             return false;
11714         }
11715         q = qe.query;
11716         
11717         forceAll = qe.forceAll;
11718         if(forceAll === true || (q.length >= this.minChars)){
11719             
11720             this.hasQuery = true;
11721             
11722             if(this.lastQuery != q || this.alwaysQuery){
11723                 this.lastQuery = q;
11724                 if(this.mode == 'local'){
11725                     this.selectedIndex = -1;
11726                     if(forceAll){
11727                         this.store.clearFilter();
11728                     }else{
11729                         this.store.filter(this.displayField, q);
11730                     }
11731                     this.onLoad();
11732                 }else{
11733                     this.store.baseParams[this.queryParam] = q;
11734                     
11735                     var options = {params : this.getParams(q)};
11736                     
11737                     if(this.loadNext){
11738                         options.add = true;
11739                         options.params.start = this.page * this.pageSize;
11740                     }
11741                     
11742                     this.store.load(options);
11743                     /*
11744                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
11745                      *  we should expand the list on onLoad
11746                      *  so command out it
11747                      */
11748 //                    this.expand();
11749                 }
11750             }else{
11751                 this.selectedIndex = -1;
11752                 this.onLoad();   
11753             }
11754         }
11755         
11756         this.loadNext = false;
11757     },
11758
11759     // private
11760     getParams : function(q){
11761         var p = {};
11762         //p[this.queryParam] = q;
11763         
11764         if(this.pageSize){
11765             p.start = 0;
11766             p.limit = this.pageSize;
11767         }
11768         return p;
11769     },
11770
11771     /**
11772      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
11773      */
11774     collapse : function(){
11775         if(!this.isExpanded()){
11776             return;
11777         }
11778         
11779         this.list.hide();
11780         
11781         if(this.tickable){
11782             this.okBtn.hide();
11783             this.cancelBtn.hide();
11784             this.trigger.show();
11785         }
11786         
11787         Roo.get(document).un('mousedown', this.collapseIf, this);
11788         Roo.get(document).un('mousewheel', this.collapseIf, this);
11789         if (!this.editable) {
11790             Roo.get(document).un('keydown', this.listKeyPress, this);
11791         }
11792         this.fireEvent('collapse', this);
11793     },
11794
11795     // private
11796     collapseIf : function(e){
11797         var in_combo  = e.within(this.el);
11798         var in_list =  e.within(this.list);
11799         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
11800         
11801         if (in_combo || in_list || is_list) {
11802             //e.stopPropagation();
11803             return;
11804         }
11805         
11806         if(this.tickable){
11807             this.onTickableFooterButtonClick(e, false, false);
11808         }
11809
11810         this.collapse();
11811         
11812     },
11813
11814     /**
11815      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
11816      */
11817     expand : function(){
11818        
11819         if(this.isExpanded() || !this.hasFocus){
11820             return;
11821         }
11822         
11823         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
11824         this.list.setWidth(lw);
11825         
11826         
11827          Roo.log('expand');
11828         
11829         this.list.show();
11830         
11831         this.restrictHeight();
11832         
11833         if(this.tickable){
11834             
11835             this.tickItems = Roo.apply([], this.item);
11836             
11837             this.okBtn.show();
11838             this.cancelBtn.show();
11839             this.trigger.hide();
11840             
11841         }
11842         
11843         Roo.get(document).on('mousedown', this.collapseIf, this);
11844         Roo.get(document).on('mousewheel', this.collapseIf, this);
11845         if (!this.editable) {
11846             Roo.get(document).on('keydown', this.listKeyPress, this);
11847         }
11848         
11849         this.fireEvent('expand', this);
11850     },
11851
11852     // private
11853     // Implements the default empty TriggerField.onTriggerClick function
11854     onTriggerClick : function(e)
11855     {
11856         Roo.log('trigger click');
11857         
11858         if(this.disabled || !this.triggerList){
11859             return;
11860         }
11861         
11862         this.page = 0;
11863         this.loadNext = false;
11864         
11865         if(this.isExpanded()){
11866             this.collapse();
11867             if (!this.blockFocus) {
11868                 this.inputEl().focus();
11869             }
11870             
11871         }else {
11872             this.hasFocus = true;
11873             if(this.triggerAction == 'all') {
11874                 this.doQuery(this.allQuery, true);
11875             } else {
11876                 this.doQuery(this.getRawValue());
11877             }
11878             if (!this.blockFocus) {
11879                 this.inputEl().focus();
11880             }
11881         }
11882     },
11883     
11884     onTickableTriggerClick : function(e)
11885     {
11886         if(this.disabled){
11887             return;
11888         }
11889         
11890         this.page = 0;
11891         this.loadNext = false;
11892         this.hasFocus = true;
11893         
11894         if(this.triggerAction == 'all') {
11895             this.doQuery(this.allQuery, true);
11896         } else {
11897             this.doQuery(this.getRawValue());
11898         }
11899     },
11900     
11901     onSearchFieldClick : function(e)
11902     {
11903         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
11904             return;
11905         }
11906         
11907         this.page = 0;
11908         this.loadNext = false;
11909         this.hasFocus = true;
11910         
11911         if(this.triggerAction == 'all') {
11912             this.doQuery(this.allQuery, true);
11913         } else {
11914             this.doQuery(this.getRawValue());
11915         }
11916     },
11917     
11918     listKeyPress : function(e)
11919     {
11920         //Roo.log('listkeypress');
11921         // scroll to first matching element based on key pres..
11922         if (e.isSpecialKey()) {
11923             return false;
11924         }
11925         var k = String.fromCharCode(e.getKey()).toUpperCase();
11926         //Roo.log(k);
11927         var match  = false;
11928         var csel = this.view.getSelectedNodes();
11929         var cselitem = false;
11930         if (csel.length) {
11931             var ix = this.view.indexOf(csel[0]);
11932             cselitem  = this.store.getAt(ix);
11933             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
11934                 cselitem = false;
11935             }
11936             
11937         }
11938         
11939         this.store.each(function(v) { 
11940             if (cselitem) {
11941                 // start at existing selection.
11942                 if (cselitem.id == v.id) {
11943                     cselitem = false;
11944                 }
11945                 return true;
11946             }
11947                 
11948             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
11949                 match = this.store.indexOf(v);
11950                 return false;
11951             }
11952             return true;
11953         }, this);
11954         
11955         if (match === false) {
11956             return true; // no more action?
11957         }
11958         // scroll to?
11959         this.view.select(match);
11960         var sn = Roo.get(this.view.getSelectedNodes()[0])
11961         sn.scrollIntoView(sn.dom.parentNode, false);
11962     },
11963     
11964     onViewScroll : function(e, t){
11965         
11966         if(this.view.el.getScroll().top == 0 ||this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
11967             return;
11968         }
11969         
11970         this.hasQuery = true;
11971         
11972         this.loading = this.list.select('.loading', true).first();
11973         
11974         if(this.loading === null){
11975             this.list.createChild({
11976                 tag: 'div',
11977                 cls: 'loading select2-more-results select2-active',
11978                 html: 'Loading more results...'
11979             })
11980             
11981             this.loading = this.list.select('.loading', true).first();
11982             
11983             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
11984             
11985             this.loading.hide();
11986         }
11987         
11988         this.loading.show();
11989         
11990         var _combo = this;
11991         
11992         this.page++;
11993         this.loadNext = true;
11994         
11995         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
11996         
11997         return;
11998     },
11999     
12000     addItem : function(o)
12001     {   
12002         var dv = ''; // display value
12003         
12004         if (this.displayField) {
12005             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12006         } else {
12007             // this is an error condition!!!
12008             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
12009         }
12010         
12011         if(!dv.length){
12012             return;
12013         }
12014         
12015         var choice = this.choices.createChild({
12016             tag: 'li',
12017             cls: 'select2-search-choice',
12018             cn: [
12019                 {
12020                     tag: 'div',
12021                     html: dv
12022                 },
12023                 {
12024                     tag: 'a',
12025                     href: '#',
12026                     cls: 'select2-search-choice-close',
12027                     tabindex: '-1'
12028                 }
12029             ]
12030             
12031         }, this.searchField);
12032         
12033         var close = choice.select('a.select2-search-choice-close', true).first()
12034         
12035         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
12036         
12037         this.item.push(o);
12038         
12039         this.lastData = o;
12040         
12041         this.syncValue();
12042         
12043         this.inputEl().dom.value = '';
12044         
12045     },
12046     
12047     onRemoveItem : function(e, _self, o)
12048     {
12049         e.preventDefault();
12050         var index = this.item.indexOf(o.data) * 1;
12051         
12052         if( index < 0){
12053             Roo.log('not this item?!');
12054             return;
12055         }
12056         
12057         this.item.splice(index, 1);
12058         o.item.remove();
12059         
12060         this.syncValue();
12061         
12062         this.fireEvent('remove', this, e);
12063         
12064     },
12065     
12066     syncValue : function()
12067     {
12068         if(!this.item.length){
12069             this.clearValue();
12070             return;
12071         }
12072             
12073         var value = [];
12074         var _this = this;
12075         Roo.each(this.item, function(i){
12076             if(_this.valueField){
12077                 value.push(i[_this.valueField]);
12078                 return;
12079             }
12080
12081             value.push(i);
12082         });
12083
12084         this.value = value.join(',');
12085
12086         if(this.hiddenField){
12087             this.hiddenField.dom.value = this.value;
12088         }
12089     },
12090     
12091     clearItem : function()
12092     {
12093         if(!this.multiple){
12094             return;
12095         }
12096         
12097         this.item = [];
12098         
12099         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
12100            c.remove();
12101         });
12102         
12103         this.syncValue();
12104     },
12105     
12106     inputEl: function ()
12107     {
12108         if(this.tickable){
12109             return this.searchField;
12110         }
12111         return this.el.select('input.form-control',true).first();
12112     },
12113     
12114     
12115     onTickableFooterButtonClick : function(e, btn, el)
12116     {
12117         e.preventDefault();
12118         
12119         if(btn && btn.name == 'cancel'){
12120             this.tickItems = Roo.apply([], this.item);
12121             this.collapse();
12122             return;
12123         }
12124         
12125         this.clearItem();
12126         
12127         var _this = this;
12128         
12129         Roo.each(this.tickItems, function(o){
12130             _this.addItem(o);
12131         });
12132         
12133         this.collapse();
12134         
12135     },
12136     
12137     validate : function()
12138     {
12139         var v = this.getRawValue();
12140         
12141         if(this.multiple){
12142             v = this.getValue();
12143         }
12144         
12145         if(this.disabled || this.validateValue(v)){
12146             this.clearInvalid();
12147             return true;
12148         }
12149         return false;
12150     }
12151     
12152     
12153
12154     /** 
12155     * @cfg {Boolean} grow 
12156     * @hide 
12157     */
12158     /** 
12159     * @cfg {Number} growMin 
12160     * @hide 
12161     */
12162     /** 
12163     * @cfg {Number} growMax 
12164     * @hide 
12165     */
12166     /**
12167      * @hide
12168      * @method autoSize
12169      */
12170 });
12171 /*
12172  * Based on:
12173  * Ext JS Library 1.1.1
12174  * Copyright(c) 2006-2007, Ext JS, LLC.
12175  *
12176  * Originally Released Under LGPL - original licence link has changed is not relivant.
12177  *
12178  * Fork - LGPL
12179  * <script type="text/javascript">
12180  */
12181
12182 /**
12183  * @class Roo.View
12184  * @extends Roo.util.Observable
12185  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
12186  * This class also supports single and multi selection modes. <br>
12187  * Create a data model bound view:
12188  <pre><code>
12189  var store = new Roo.data.Store(...);
12190
12191  var view = new Roo.View({
12192     el : "my-element",
12193     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
12194  
12195     singleSelect: true,
12196     selectedClass: "ydataview-selected",
12197     store: store
12198  });
12199
12200  // listen for node click?
12201  view.on("click", function(vw, index, node, e){
12202  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
12203  });
12204
12205  // load XML data
12206  dataModel.load("foobar.xml");
12207  </code></pre>
12208  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
12209  * <br><br>
12210  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
12211  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
12212  * 
12213  * Note: old style constructor is still suported (container, template, config)
12214  * 
12215  * @constructor
12216  * Create a new View
12217  * @param {Object} config The config object
12218  * 
12219  */
12220 Roo.View = function(config, depreciated_tpl, depreciated_config){
12221     
12222     this.parent = false;
12223     
12224     if (typeof(depreciated_tpl) == 'undefined') {
12225         // new way.. - universal constructor.
12226         Roo.apply(this, config);
12227         this.el  = Roo.get(this.el);
12228     } else {
12229         // old format..
12230         this.el  = Roo.get(config);
12231         this.tpl = depreciated_tpl;
12232         Roo.apply(this, depreciated_config);
12233     }
12234     this.wrapEl  = this.el.wrap().wrap();
12235     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
12236     
12237     
12238     if(typeof(this.tpl) == "string"){
12239         this.tpl = new Roo.Template(this.tpl);
12240     } else {
12241         // support xtype ctors..
12242         this.tpl = new Roo.factory(this.tpl, Roo);
12243     }
12244     
12245     
12246     this.tpl.compile();
12247     
12248     /** @private */
12249     this.addEvents({
12250         /**
12251          * @event beforeclick
12252          * Fires before a click is processed. Returns false to cancel the default action.
12253          * @param {Roo.View} this
12254          * @param {Number} index The index of the target node
12255          * @param {HTMLElement} node The target node
12256          * @param {Roo.EventObject} e The raw event object
12257          */
12258             "beforeclick" : true,
12259         /**
12260          * @event click
12261          * Fires when a template node is clicked.
12262          * @param {Roo.View} this
12263          * @param {Number} index The index of the target node
12264          * @param {HTMLElement} node The target node
12265          * @param {Roo.EventObject} e The raw event object
12266          */
12267             "click" : true,
12268         /**
12269          * @event dblclick
12270          * Fires when a template node is double clicked.
12271          * @param {Roo.View} this
12272          * @param {Number} index The index of the target node
12273          * @param {HTMLElement} node The target node
12274          * @param {Roo.EventObject} e The raw event object
12275          */
12276             "dblclick" : true,
12277         /**
12278          * @event contextmenu
12279          * Fires when a template node is right clicked.
12280          * @param {Roo.View} this
12281          * @param {Number} index The index of the target node
12282          * @param {HTMLElement} node The target node
12283          * @param {Roo.EventObject} e The raw event object
12284          */
12285             "contextmenu" : true,
12286         /**
12287          * @event selectionchange
12288          * Fires when the selected nodes change.
12289          * @param {Roo.View} this
12290          * @param {Array} selections Array of the selected nodes
12291          */
12292             "selectionchange" : true,
12293     
12294         /**
12295          * @event beforeselect
12296          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
12297          * @param {Roo.View} this
12298          * @param {HTMLElement} node The node to be selected
12299          * @param {Array} selections Array of currently selected nodes
12300          */
12301             "beforeselect" : true,
12302         /**
12303          * @event preparedata
12304          * Fires on every row to render, to allow you to change the data.
12305          * @param {Roo.View} this
12306          * @param {Object} data to be rendered (change this)
12307          */
12308           "preparedata" : true
12309           
12310           
12311         });
12312
12313
12314
12315     this.el.on({
12316         "click": this.onClick,
12317         "dblclick": this.onDblClick,
12318         "contextmenu": this.onContextMenu,
12319         scope:this
12320     });
12321
12322     this.selections = [];
12323     this.nodes = [];
12324     this.cmp = new Roo.CompositeElementLite([]);
12325     if(this.store){
12326         this.store = Roo.factory(this.store, Roo.data);
12327         this.setStore(this.store, true);
12328     }
12329     
12330     if ( this.footer && this.footer.xtype) {
12331            
12332          var fctr = this.wrapEl.appendChild(document.createElement("div"));
12333         
12334         this.footer.dataSource = this.store
12335         this.footer.container = fctr;
12336         this.footer = Roo.factory(this.footer, Roo);
12337         fctr.insertFirst(this.el);
12338         
12339         // this is a bit insane - as the paging toolbar seems to detach the el..
12340 //        dom.parentNode.parentNode.parentNode
12341          // they get detached?
12342     }
12343     
12344     
12345     Roo.View.superclass.constructor.call(this);
12346     
12347     
12348 };
12349
12350 Roo.extend(Roo.View, Roo.util.Observable, {
12351     
12352      /**
12353      * @cfg {Roo.data.Store} store Data store to load data from.
12354      */
12355     store : false,
12356     
12357     /**
12358      * @cfg {String|Roo.Element} el The container element.
12359      */
12360     el : '',
12361     
12362     /**
12363      * @cfg {String|Roo.Template} tpl The template used by this View 
12364      */
12365     tpl : false,
12366     /**
12367      * @cfg {String} dataName the named area of the template to use as the data area
12368      *                          Works with domtemplates roo-name="name"
12369      */
12370     dataName: false,
12371     /**
12372      * @cfg {String} selectedClass The css class to add to selected nodes
12373      */
12374     selectedClass : "x-view-selected",
12375      /**
12376      * @cfg {String} emptyText The empty text to show when nothing is loaded.
12377      */
12378     emptyText : "",
12379     
12380     /**
12381      * @cfg {String} text to display on mask (default Loading)
12382      */
12383     mask : false,
12384     /**
12385      * @cfg {Boolean} multiSelect Allow multiple selection
12386      */
12387     multiSelect : false,
12388     /**
12389      * @cfg {Boolean} singleSelect Allow single selection
12390      */
12391     singleSelect:  false,
12392     
12393     /**
12394      * @cfg {Boolean} toggleSelect - selecting 
12395      */
12396     toggleSelect : false,
12397     
12398     /**
12399      * @cfg {Boolean} tickable - selecting 
12400      */
12401     tickable : false,
12402     
12403     /**
12404      * Returns the element this view is bound to.
12405      * @return {Roo.Element}
12406      */
12407     getEl : function(){
12408         return this.wrapEl;
12409     },
12410     
12411     
12412
12413     /**
12414      * Refreshes the view. - called by datachanged on the store. - do not call directly.
12415      */
12416     refresh : function(){
12417         //Roo.log('refresh');
12418         var t = this.tpl;
12419         
12420         // if we are using something like 'domtemplate', then
12421         // the what gets used is:
12422         // t.applySubtemplate(NAME, data, wrapping data..)
12423         // the outer template then get' applied with
12424         //     the store 'extra data'
12425         // and the body get's added to the
12426         //      roo-name="data" node?
12427         //      <span class='roo-tpl-{name}'></span> ?????
12428         
12429         
12430         
12431         this.clearSelections();
12432         this.el.update("");
12433         var html = [];
12434         var records = this.store.getRange();
12435         if(records.length < 1) {
12436             
12437             // is this valid??  = should it render a template??
12438             
12439             this.el.update(this.emptyText);
12440             return;
12441         }
12442         var el = this.el;
12443         if (this.dataName) {
12444             this.el.update(t.apply(this.store.meta)); //????
12445             el = this.el.child('.roo-tpl-' + this.dataName);
12446         }
12447         
12448         for(var i = 0, len = records.length; i < len; i++){
12449             var data = this.prepareData(records[i].data, i, records[i]);
12450             this.fireEvent("preparedata", this, data, i, records[i]);
12451             
12452             var d = Roo.apply({}, data);
12453             
12454             if(this.tickable){
12455                 Roo.apply(d, {'roo-id' : Roo.id()});
12456                 
12457                 var _this = this;
12458             
12459                 Roo.each(this.parent.item, function(item){
12460                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
12461                         return;
12462                     }
12463                     Roo.apply(d, {'roo-data-checked' : 'checked'});
12464                 });
12465             }
12466             
12467             html[html.length] = Roo.util.Format.trim(
12468                 this.dataName ?
12469                     t.applySubtemplate(this.dataName, d, this.store.meta) :
12470                     t.apply(d)
12471             );
12472         }
12473         
12474         
12475         
12476         el.update(html.join(""));
12477         this.nodes = el.dom.childNodes;
12478         this.updateIndexes(0);
12479     },
12480     
12481
12482     /**
12483      * Function to override to reformat the data that is sent to
12484      * the template for each node.
12485      * DEPRICATED - use the preparedata event handler.
12486      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
12487      * a JSON object for an UpdateManager bound view).
12488      */
12489     prepareData : function(data, index, record)
12490     {
12491         this.fireEvent("preparedata", this, data, index, record);
12492         return data;
12493     },
12494
12495     onUpdate : function(ds, record){
12496         // Roo.log('on update');   
12497         this.clearSelections();
12498         var index = this.store.indexOf(record);
12499         var n = this.nodes[index];
12500         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
12501         n.parentNode.removeChild(n);
12502         this.updateIndexes(index, index);
12503     },
12504
12505     
12506     
12507 // --------- FIXME     
12508     onAdd : function(ds, records, index)
12509     {
12510         //Roo.log(['on Add', ds, records, index] );        
12511         this.clearSelections();
12512         if(this.nodes.length == 0){
12513             this.refresh();
12514             return;
12515         }
12516         var n = this.nodes[index];
12517         for(var i = 0, len = records.length; i < len; i++){
12518             var d = this.prepareData(records[i].data, i, records[i]);
12519             if(n){
12520                 this.tpl.insertBefore(n, d);
12521             }else{
12522                 
12523                 this.tpl.append(this.el, d);
12524             }
12525         }
12526         this.updateIndexes(index);
12527     },
12528
12529     onRemove : function(ds, record, index){
12530        // Roo.log('onRemove');
12531         this.clearSelections();
12532         var el = this.dataName  ?
12533             this.el.child('.roo-tpl-' + this.dataName) :
12534             this.el; 
12535         
12536         el.dom.removeChild(this.nodes[index]);
12537         this.updateIndexes(index);
12538     },
12539
12540     /**
12541      * Refresh an individual node.
12542      * @param {Number} index
12543      */
12544     refreshNode : function(index){
12545         this.onUpdate(this.store, this.store.getAt(index));
12546     },
12547
12548     updateIndexes : function(startIndex, endIndex){
12549         var ns = this.nodes;
12550         startIndex = startIndex || 0;
12551         endIndex = endIndex || ns.length - 1;
12552         for(var i = startIndex; i <= endIndex; i++){
12553             ns[i].nodeIndex = i;
12554         }
12555     },
12556
12557     /**
12558      * Changes the data store this view uses and refresh the view.
12559      * @param {Store} store
12560      */
12561     setStore : function(store, initial){
12562         if(!initial && this.store){
12563             this.store.un("datachanged", this.refresh);
12564             this.store.un("add", this.onAdd);
12565             this.store.un("remove", this.onRemove);
12566             this.store.un("update", this.onUpdate);
12567             this.store.un("clear", this.refresh);
12568             this.store.un("beforeload", this.onBeforeLoad);
12569             this.store.un("load", this.onLoad);
12570             this.store.un("loadexception", this.onLoad);
12571         }
12572         if(store){
12573           
12574             store.on("datachanged", this.refresh, this);
12575             store.on("add", this.onAdd, this);
12576             store.on("remove", this.onRemove, this);
12577             store.on("update", this.onUpdate, this);
12578             store.on("clear", this.refresh, this);
12579             store.on("beforeload", this.onBeforeLoad, this);
12580             store.on("load", this.onLoad, this);
12581             store.on("loadexception", this.onLoad, this);
12582         }
12583         
12584         if(store){
12585             this.refresh();
12586         }
12587     },
12588     /**
12589      * onbeforeLoad - masks the loading area.
12590      *
12591      */
12592     onBeforeLoad : function(store,opts)
12593     {
12594          //Roo.log('onBeforeLoad');   
12595         if (!opts.add) {
12596             this.el.update("");
12597         }
12598         this.el.mask(this.mask ? this.mask : "Loading" ); 
12599     },
12600     onLoad : function ()
12601     {
12602         this.el.unmask();
12603     },
12604     
12605
12606     /**
12607      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
12608      * @param {HTMLElement} node
12609      * @return {HTMLElement} The template node
12610      */
12611     findItemFromChild : function(node){
12612         var el = this.dataName  ?
12613             this.el.child('.roo-tpl-' + this.dataName,true) :
12614             this.el.dom; 
12615         
12616         if(!node || node.parentNode == el){
12617                     return node;
12618             }
12619             var p = node.parentNode;
12620             while(p && p != el){
12621             if(p.parentNode == el){
12622                 return p;
12623             }
12624             p = p.parentNode;
12625         }
12626             return null;
12627     },
12628
12629     /** @ignore */
12630     onClick : function(e){
12631         var item = this.findItemFromChild(e.getTarget());
12632         if(item){
12633             var index = this.indexOf(item);
12634             if(this.onItemClick(item, index, e) !== false){
12635                 this.fireEvent("click", this, index, item, e);
12636             }
12637         }else{
12638             this.clearSelections();
12639         }
12640     },
12641
12642     /** @ignore */
12643     onContextMenu : function(e){
12644         var item = this.findItemFromChild(e.getTarget());
12645         if(item){
12646             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
12647         }
12648     },
12649
12650     /** @ignore */
12651     onDblClick : function(e){
12652         var item = this.findItemFromChild(e.getTarget());
12653         if(item){
12654             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
12655         }
12656     },
12657
12658     onItemClick : function(item, index, e)
12659     {
12660         if(this.fireEvent("beforeclick", this, index, item, e) === false){
12661             return false;
12662         }
12663         if (this.toggleSelect) {
12664             var m = this.isSelected(item) ? 'unselect' : 'select';
12665             //Roo.log(m);
12666             var _t = this;
12667             _t[m](item, true, false);
12668             return true;
12669         }
12670         if(this.multiSelect || this.singleSelect){
12671             if(this.multiSelect && e.shiftKey && this.lastSelection){
12672                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
12673             }else{
12674                 this.select(item, this.multiSelect && e.ctrlKey);
12675                 this.lastSelection = item;
12676             }
12677             
12678             if(!this.tickable){
12679                 e.preventDefault();
12680             }
12681             
12682         }
12683         return true;
12684     },
12685
12686     /**
12687      * Get the number of selected nodes.
12688      * @return {Number}
12689      */
12690     getSelectionCount : function(){
12691         return this.selections.length;
12692     },
12693
12694     /**
12695      * Get the currently selected nodes.
12696      * @return {Array} An array of HTMLElements
12697      */
12698     getSelectedNodes : function(){
12699         return this.selections;
12700     },
12701
12702     /**
12703      * Get the indexes of the selected nodes.
12704      * @return {Array}
12705      */
12706     getSelectedIndexes : function(){
12707         var indexes = [], s = this.selections;
12708         for(var i = 0, len = s.length; i < len; i++){
12709             indexes.push(s[i].nodeIndex);
12710         }
12711         return indexes;
12712     },
12713
12714     /**
12715      * Clear all selections
12716      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
12717      */
12718     clearSelections : function(suppressEvent){
12719         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
12720             this.cmp.elements = this.selections;
12721             this.cmp.removeClass(this.selectedClass);
12722             this.selections = [];
12723             if(!suppressEvent){
12724                 this.fireEvent("selectionchange", this, this.selections);
12725             }
12726         }
12727     },
12728
12729     /**
12730      * Returns true if the passed node is selected
12731      * @param {HTMLElement/Number} node The node or node index
12732      * @return {Boolean}
12733      */
12734     isSelected : function(node){
12735         var s = this.selections;
12736         if(s.length < 1){
12737             return false;
12738         }
12739         node = this.getNode(node);
12740         return s.indexOf(node) !== -1;
12741     },
12742
12743     /**
12744      * Selects nodes.
12745      * @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
12746      * @param {Boolean} keepExisting (optional) true to keep existing selections
12747      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12748      */
12749     select : function(nodeInfo, keepExisting, suppressEvent){
12750         if(nodeInfo instanceof Array){
12751             if(!keepExisting){
12752                 this.clearSelections(true);
12753             }
12754             for(var i = 0, len = nodeInfo.length; i < len; i++){
12755                 this.select(nodeInfo[i], true, true);
12756             }
12757             return;
12758         } 
12759         var node = this.getNode(nodeInfo);
12760         if(!node || this.isSelected(node)){
12761             return; // already selected.
12762         }
12763         if(!keepExisting){
12764             this.clearSelections(true);
12765         }
12766         
12767         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
12768             Roo.fly(node).addClass(this.selectedClass);
12769             this.selections.push(node);
12770             if(!suppressEvent){
12771                 this.fireEvent("selectionchange", this, this.selections);
12772             }
12773         }
12774         
12775         
12776     },
12777       /**
12778      * Unselects nodes.
12779      * @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
12780      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
12781      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12782      */
12783     unselect : function(nodeInfo, keepExisting, suppressEvent)
12784     {
12785         if(nodeInfo instanceof Array){
12786             Roo.each(this.selections, function(s) {
12787                 this.unselect(s, nodeInfo);
12788             }, this);
12789             return;
12790         }
12791         var node = this.getNode(nodeInfo);
12792         if(!node || !this.isSelected(node)){
12793             //Roo.log("not selected");
12794             return; // not selected.
12795         }
12796         // fireevent???
12797         var ns = [];
12798         Roo.each(this.selections, function(s) {
12799             if (s == node ) {
12800                 Roo.fly(node).removeClass(this.selectedClass);
12801
12802                 return;
12803             }
12804             ns.push(s);
12805         },this);
12806         
12807         this.selections= ns;
12808         this.fireEvent("selectionchange", this, this.selections);
12809     },
12810
12811     /**
12812      * Gets a template node.
12813      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12814      * @return {HTMLElement} The node or null if it wasn't found
12815      */
12816     getNode : function(nodeInfo){
12817         if(typeof nodeInfo == "string"){
12818             return document.getElementById(nodeInfo);
12819         }else if(typeof nodeInfo == "number"){
12820             return this.nodes[nodeInfo];
12821         }
12822         return nodeInfo;
12823     },
12824
12825     /**
12826      * Gets a range template nodes.
12827      * @param {Number} startIndex
12828      * @param {Number} endIndex
12829      * @return {Array} An array of nodes
12830      */
12831     getNodes : function(start, end){
12832         var ns = this.nodes;
12833         start = start || 0;
12834         end = typeof end == "undefined" ? ns.length - 1 : end;
12835         var nodes = [];
12836         if(start <= end){
12837             for(var i = start; i <= end; i++){
12838                 nodes.push(ns[i]);
12839             }
12840         } else{
12841             for(var i = start; i >= end; i--){
12842                 nodes.push(ns[i]);
12843             }
12844         }
12845         return nodes;
12846     },
12847
12848     /**
12849      * Finds the index of the passed node
12850      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12851      * @return {Number} The index of the node or -1
12852      */
12853     indexOf : function(node){
12854         node = this.getNode(node);
12855         if(typeof node.nodeIndex == "number"){
12856             return node.nodeIndex;
12857         }
12858         var ns = this.nodes;
12859         for(var i = 0, len = ns.length; i < len; i++){
12860             if(ns[i] == node){
12861                 return i;
12862             }
12863         }
12864         return -1;
12865     }
12866 });
12867 /*
12868  * - LGPL
12869  *
12870  * based on jquery fullcalendar
12871  * 
12872  */
12873
12874 Roo.bootstrap = Roo.bootstrap || {};
12875 /**
12876  * @class Roo.bootstrap.Calendar
12877  * @extends Roo.bootstrap.Component
12878  * Bootstrap Calendar class
12879  * @cfg {Boolean} loadMask (true|false) default false
12880  * @cfg {Object} header generate the user specific header of the calendar, default false
12881
12882  * @constructor
12883  * Create a new Container
12884  * @param {Object} config The config object
12885  */
12886
12887
12888
12889 Roo.bootstrap.Calendar = function(config){
12890     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
12891      this.addEvents({
12892         /**
12893              * @event select
12894              * Fires when a date is selected
12895              * @param {DatePicker} this
12896              * @param {Date} date The selected date
12897              */
12898         'select': true,
12899         /**
12900              * @event monthchange
12901              * Fires when the displayed month changes 
12902              * @param {DatePicker} this
12903              * @param {Date} date The selected month
12904              */
12905         'monthchange': true,
12906         /**
12907              * @event evententer
12908              * Fires when mouse over an event
12909              * @param {Calendar} this
12910              * @param {event} Event
12911              */
12912         'evententer': true,
12913         /**
12914              * @event eventleave
12915              * Fires when the mouse leaves an
12916              * @param {Calendar} this
12917              * @param {event}
12918              */
12919         'eventleave': true,
12920         /**
12921              * @event eventclick
12922              * Fires when the mouse click an
12923              * @param {Calendar} this
12924              * @param {event}
12925              */
12926         'eventclick': true
12927         
12928     });
12929
12930 };
12931
12932 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
12933     
12934      /**
12935      * @cfg {Number} startDay
12936      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
12937      */
12938     startDay : 0,
12939     
12940     loadMask : false,
12941     
12942     header : false,
12943       
12944     getAutoCreate : function(){
12945         
12946         
12947         var fc_button = function(name, corner, style, content ) {
12948             return Roo.apply({},{
12949                 tag : 'span',
12950                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
12951                          (corner.length ?
12952                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
12953                             ''
12954                         ),
12955                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
12956                 unselectable: 'on'
12957             });
12958         };
12959         
12960         var header = {};
12961         
12962         if(!this.header){
12963             header = {
12964                 tag : 'table',
12965                 cls : 'fc-header',
12966                 style : 'width:100%',
12967                 cn : [
12968                     {
12969                         tag: 'tr',
12970                         cn : [
12971                             {
12972                                 tag : 'td',
12973                                 cls : 'fc-header-left',
12974                                 cn : [
12975                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
12976                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
12977                                     { tag: 'span', cls: 'fc-header-space' },
12978                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
12979
12980
12981                                 ]
12982                             },
12983
12984                             {
12985                                 tag : 'td',
12986                                 cls : 'fc-header-center',
12987                                 cn : [
12988                                     {
12989                                         tag: 'span',
12990                                         cls: 'fc-header-title',
12991                                         cn : {
12992                                             tag: 'H2',
12993                                             html : 'month / year'
12994                                         }
12995                                     }
12996
12997                                 ]
12998                             },
12999                             {
13000                                 tag : 'td',
13001                                 cls : 'fc-header-right',
13002                                 cn : [
13003                               /*      fc_button('month', 'left', '', 'month' ),
13004                                     fc_button('week', '', '', 'week' ),
13005                                     fc_button('day', 'right', '', 'day' )
13006                                 */    
13007
13008                                 ]
13009                             }
13010
13011                         ]
13012                     }
13013                 ]
13014             };
13015         }
13016         
13017         header = this.header;
13018         
13019        
13020         var cal_heads = function() {
13021             var ret = [];
13022             // fixme - handle this.
13023             
13024             for (var i =0; i < Date.dayNames.length; i++) {
13025                 var d = Date.dayNames[i];
13026                 ret.push({
13027                     tag: 'th',
13028                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
13029                     html : d.substring(0,3)
13030                 });
13031                 
13032             }
13033             ret[0].cls += ' fc-first';
13034             ret[6].cls += ' fc-last';
13035             return ret;
13036         };
13037         var cal_cell = function(n) {
13038             return  {
13039                 tag: 'td',
13040                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
13041                 cn : [
13042                     {
13043                         cn : [
13044                             {
13045                                 cls: 'fc-day-number',
13046                                 html: 'D'
13047                             },
13048                             {
13049                                 cls: 'fc-day-content',
13050                              
13051                                 cn : [
13052                                      {
13053                                         style: 'position: relative;' // height: 17px;
13054                                     }
13055                                 ]
13056                             }
13057                             
13058                             
13059                         ]
13060                     }
13061                 ]
13062                 
13063             }
13064         };
13065         var cal_rows = function() {
13066             
13067             var ret = []
13068             for (var r = 0; r < 6; r++) {
13069                 var row= {
13070                     tag : 'tr',
13071                     cls : 'fc-week',
13072                     cn : []
13073                 };
13074                 
13075                 for (var i =0; i < Date.dayNames.length; i++) {
13076                     var d = Date.dayNames[i];
13077                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
13078
13079                 }
13080                 row.cn[0].cls+=' fc-first';
13081                 row.cn[0].cn[0].style = 'min-height:90px';
13082                 row.cn[6].cls+=' fc-last';
13083                 ret.push(row);
13084                 
13085             }
13086             ret[0].cls += ' fc-first';
13087             ret[4].cls += ' fc-prev-last';
13088             ret[5].cls += ' fc-last';
13089             return ret;
13090             
13091         };
13092         
13093         var cal_table = {
13094             tag: 'table',
13095             cls: 'fc-border-separate',
13096             style : 'width:100%',
13097             cellspacing  : 0,
13098             cn : [
13099                 { 
13100                     tag: 'thead',
13101                     cn : [
13102                         { 
13103                             tag: 'tr',
13104                             cls : 'fc-first fc-last',
13105                             cn : cal_heads()
13106                         }
13107                     ]
13108                 },
13109                 { 
13110                     tag: 'tbody',
13111                     cn : cal_rows()
13112                 }
13113                   
13114             ]
13115         };
13116          
13117          var cfg = {
13118             cls : 'fc fc-ltr',
13119             cn : [
13120                 header,
13121                 {
13122                     cls : 'fc-content',
13123                     style : "position: relative;",
13124                     cn : [
13125                         {
13126                             cls : 'fc-view fc-view-month fc-grid',
13127                             style : 'position: relative',
13128                             unselectable : 'on',
13129                             cn : [
13130                                 {
13131                                     cls : 'fc-event-container',
13132                                     style : 'position:absolute;z-index:8;top:0;left:0;'
13133                                 },
13134                                 cal_table
13135                             ]
13136                         }
13137                     ]
13138     
13139                 }
13140            ] 
13141             
13142         };
13143         
13144          
13145         
13146         return cfg;
13147     },
13148     
13149     
13150     initEvents : function()
13151     {
13152         if(!this.store){
13153             throw "can not find store for calendar";
13154         }
13155         
13156         var mark = {
13157             tag: "div",
13158             cls:"x-dlg-mask",
13159             style: "text-align:center",
13160             cn: [
13161                 {
13162                     tag: "div",
13163                     style: "background-color:white;width:50%;margin:250 auto",
13164                     cn: [
13165                         {
13166                             tag: "img",
13167                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
13168                         },
13169                         {
13170                             tag: "span",
13171                             html: "Loading"
13172                         }
13173                         
13174                     ]
13175                 }
13176             ]
13177         }
13178         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
13179         
13180         var size = this.el.select('.fc-content', true).first().getSize();
13181         this.maskEl.setSize(size.width, size.height);
13182         this.maskEl.enableDisplayMode("block");
13183         if(!this.loadMask){
13184             this.maskEl.hide();
13185         }
13186         
13187         this.store = Roo.factory(this.store, Roo.data);
13188         this.store.on('load', this.onLoad, this);
13189         this.store.on('beforeload', this.onBeforeLoad, this);
13190         
13191         this.resize();
13192         
13193         this.cells = this.el.select('.fc-day',true);
13194         //Roo.log(this.cells);
13195         this.textNodes = this.el.query('.fc-day-number');
13196         this.cells.addClassOnOver('fc-state-hover');
13197         
13198         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
13199         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
13200         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
13201         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
13202         
13203         this.on('monthchange', this.onMonthChange, this);
13204         
13205         this.update(new Date().clearTime());
13206     },
13207     
13208     resize : function() {
13209         var sz  = this.el.getSize();
13210         
13211         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
13212         this.el.select('.fc-day-content div',true).setHeight(34);
13213     },
13214     
13215     
13216     // private
13217     showPrevMonth : function(e){
13218         this.update(this.activeDate.add("mo", -1));
13219     },
13220     showToday : function(e){
13221         this.update(new Date().clearTime());
13222     },
13223     // private
13224     showNextMonth : function(e){
13225         this.update(this.activeDate.add("mo", 1));
13226     },
13227
13228     // private
13229     showPrevYear : function(){
13230         this.update(this.activeDate.add("y", -1));
13231     },
13232
13233     // private
13234     showNextYear : function(){
13235         this.update(this.activeDate.add("y", 1));
13236     },
13237
13238     
13239    // private
13240     update : function(date)
13241     {
13242         var vd = this.activeDate;
13243         this.activeDate = date;
13244 //        if(vd && this.el){
13245 //            var t = date.getTime();
13246 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
13247 //                Roo.log('using add remove');
13248 //                
13249 //                this.fireEvent('monthchange', this, date);
13250 //                
13251 //                this.cells.removeClass("fc-state-highlight");
13252 //                this.cells.each(function(c){
13253 //                   if(c.dateValue == t){
13254 //                       c.addClass("fc-state-highlight");
13255 //                       setTimeout(function(){
13256 //                            try{c.dom.firstChild.focus();}catch(e){}
13257 //                       }, 50);
13258 //                       return false;
13259 //                   }
13260 //                   return true;
13261 //                });
13262 //                return;
13263 //            }
13264 //        }
13265         
13266         var days = date.getDaysInMonth();
13267         
13268         var firstOfMonth = date.getFirstDateOfMonth();
13269         var startingPos = firstOfMonth.getDay()-this.startDay;
13270         
13271         if(startingPos < this.startDay){
13272             startingPos += 7;
13273         }
13274         
13275         var pm = date.add(Date.MONTH, -1);
13276         var prevStart = pm.getDaysInMonth()-startingPos;
13277 //        
13278         this.cells = this.el.select('.fc-day',true);
13279         this.textNodes = this.el.query('.fc-day-number');
13280         this.cells.addClassOnOver('fc-state-hover');
13281         
13282         var cells = this.cells.elements;
13283         var textEls = this.textNodes;
13284         
13285         Roo.each(cells, function(cell){
13286             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
13287         });
13288         
13289         days += startingPos;
13290
13291         // convert everything to numbers so it's fast
13292         var day = 86400000;
13293         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
13294         //Roo.log(d);
13295         //Roo.log(pm);
13296         //Roo.log(prevStart);
13297         
13298         var today = new Date().clearTime().getTime();
13299         var sel = date.clearTime().getTime();
13300         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
13301         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
13302         var ddMatch = this.disabledDatesRE;
13303         var ddText = this.disabledDatesText;
13304         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
13305         var ddaysText = this.disabledDaysText;
13306         var format = this.format;
13307         
13308         var setCellClass = function(cal, cell){
13309             cell.row = 0;
13310             cell.events = [];
13311             cell.more = [];
13312             //Roo.log('set Cell Class');
13313             cell.title = "";
13314             var t = d.getTime();
13315             
13316             //Roo.log(d);
13317             
13318             cell.dateValue = t;
13319             if(t == today){
13320                 cell.className += " fc-today";
13321                 cell.className += " fc-state-highlight";
13322                 cell.title = cal.todayText;
13323             }
13324             if(t == sel){
13325                 // disable highlight in other month..
13326                 //cell.className += " fc-state-highlight";
13327                 
13328             }
13329             // disabling
13330             if(t < min) {
13331                 cell.className = " fc-state-disabled";
13332                 cell.title = cal.minText;
13333                 return;
13334             }
13335             if(t > max) {
13336                 cell.className = " fc-state-disabled";
13337                 cell.title = cal.maxText;
13338                 return;
13339             }
13340             if(ddays){
13341                 if(ddays.indexOf(d.getDay()) != -1){
13342                     cell.title = ddaysText;
13343                     cell.className = " fc-state-disabled";
13344                 }
13345             }
13346             if(ddMatch && format){
13347                 var fvalue = d.dateFormat(format);
13348                 if(ddMatch.test(fvalue)){
13349                     cell.title = ddText.replace("%0", fvalue);
13350                     cell.className = " fc-state-disabled";
13351                 }
13352             }
13353             
13354             if (!cell.initialClassName) {
13355                 cell.initialClassName = cell.dom.className;
13356             }
13357             
13358             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
13359         };
13360
13361         var i = 0;
13362         
13363         for(; i < startingPos; i++) {
13364             textEls[i].innerHTML = (++prevStart);
13365             d.setDate(d.getDate()+1);
13366             
13367             cells[i].className = "fc-past fc-other-month";
13368             setCellClass(this, cells[i]);
13369         }
13370         
13371         var intDay = 0;
13372         
13373         for(; i < days; i++){
13374             intDay = i - startingPos + 1;
13375             textEls[i].innerHTML = (intDay);
13376             d.setDate(d.getDate()+1);
13377             
13378             cells[i].className = ''; // "x-date-active";
13379             setCellClass(this, cells[i]);
13380         }
13381         var extraDays = 0;
13382         
13383         for(; i < 42; i++) {
13384             textEls[i].innerHTML = (++extraDays);
13385             d.setDate(d.getDate()+1);
13386             
13387             cells[i].className = "fc-future fc-other-month";
13388             setCellClass(this, cells[i]);
13389         }
13390         
13391         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
13392         
13393         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
13394         
13395         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
13396         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
13397         
13398         if(totalRows != 6){
13399             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
13400             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
13401         }
13402         
13403         this.fireEvent('monthchange', this, date);
13404         
13405         
13406         /*
13407         if(!this.internalRender){
13408             var main = this.el.dom.firstChild;
13409             var w = main.offsetWidth;
13410             this.el.setWidth(w + this.el.getBorderWidth("lr"));
13411             Roo.fly(main).setWidth(w);
13412             this.internalRender = true;
13413             // opera does not respect the auto grow header center column
13414             // then, after it gets a width opera refuses to recalculate
13415             // without a second pass
13416             if(Roo.isOpera && !this.secondPass){
13417                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
13418                 this.secondPass = true;
13419                 this.update.defer(10, this, [date]);
13420             }
13421         }
13422         */
13423         
13424     },
13425     
13426     findCell : function(dt) {
13427         dt = dt.clearTime().getTime();
13428         var ret = false;
13429         this.cells.each(function(c){
13430             //Roo.log("check " +c.dateValue + '?=' + dt);
13431             if(c.dateValue == dt){
13432                 ret = c;
13433                 return false;
13434             }
13435             return true;
13436         });
13437         
13438         return ret;
13439     },
13440     
13441     findCells : function(ev) {
13442         var s = ev.start.clone().clearTime().getTime();
13443        // Roo.log(s);
13444         var e= ev.end.clone().clearTime().getTime();
13445        // Roo.log(e);
13446         var ret = [];
13447         this.cells.each(function(c){
13448              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
13449             
13450             if(c.dateValue > e){
13451                 return ;
13452             }
13453             if(c.dateValue < s){
13454                 return ;
13455             }
13456             ret.push(c);
13457         });
13458         
13459         return ret;    
13460     },
13461     
13462 //    findBestRow: function(cells)
13463 //    {
13464 //        var ret = 0;
13465 //        
13466 //        for (var i =0 ; i < cells.length;i++) {
13467 //            ret  = Math.max(cells[i].rows || 0,ret);
13468 //        }
13469 //        return ret;
13470 //        
13471 //    },
13472     
13473     
13474     addItem : function(ev)
13475     {
13476         // look for vertical location slot in
13477         var cells = this.findCells(ev);
13478         
13479 //        ev.row = this.findBestRow(cells);
13480         
13481         // work out the location.
13482         
13483         var crow = false;
13484         var rows = [];
13485         for(var i =0; i < cells.length; i++) {
13486             
13487             cells[i].row = cells[0].row;
13488             
13489             if(i == 0){
13490                 cells[i].row = cells[i].row + 1;
13491             }
13492             
13493             if (!crow) {
13494                 crow = {
13495                     start : cells[i],
13496                     end :  cells[i]
13497                 };
13498                 continue;
13499             }
13500             if (crow.start.getY() == cells[i].getY()) {
13501                 // on same row.
13502                 crow.end = cells[i];
13503                 continue;
13504             }
13505             // different row.
13506             rows.push(crow);
13507             crow = {
13508                 start: cells[i],
13509                 end : cells[i]
13510             };
13511             
13512         }
13513         
13514         rows.push(crow);
13515         ev.els = [];
13516         ev.rows = rows;
13517         ev.cells = cells;
13518         
13519         cells[0].events.push(ev);
13520         
13521         this.calevents.push(ev);
13522     },
13523     
13524     clearEvents: function() {
13525         
13526         if(!this.calevents){
13527             return;
13528         }
13529         
13530         Roo.each(this.cells.elements, function(c){
13531             c.row = 0;
13532             c.events = [];
13533             c.more = [];
13534         });
13535         
13536         Roo.each(this.calevents, function(e) {
13537             Roo.each(e.els, function(el) {
13538                 el.un('mouseenter' ,this.onEventEnter, this);
13539                 el.un('mouseleave' ,this.onEventLeave, this);
13540                 el.remove();
13541             },this);
13542         },this);
13543         
13544         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
13545             e.remove();
13546         });
13547         
13548     },
13549     
13550     renderEvents: function()
13551     {   
13552         var _this = this;
13553         
13554         this.cells.each(function(c) {
13555             
13556             if(c.row < 5){
13557                 return;
13558             }
13559             
13560             var ev = c.events;
13561             
13562             var r = 4;
13563             if(c.row != c.events.length){
13564                 r = 4 - (4 - (c.row - c.events.length));
13565             }
13566             
13567             c.events = ev.slice(0, r);
13568             c.more = ev.slice(r);
13569             
13570             if(c.more.length && c.more.length == 1){
13571                 c.events.push(c.more.pop());
13572             }
13573             
13574             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
13575             
13576         });
13577             
13578         this.cells.each(function(c) {
13579             
13580             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
13581             
13582             
13583             for (var e = 0; e < c.events.length; e++){
13584                 var ev = c.events[e];
13585                 var rows = ev.rows;
13586                 
13587                 for(var i = 0; i < rows.length; i++) {
13588                 
13589                     // how many rows should it span..
13590
13591                     var  cfg = {
13592                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
13593                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
13594
13595                         unselectable : "on",
13596                         cn : [
13597                             {
13598                                 cls: 'fc-event-inner',
13599                                 cn : [
13600     //                                {
13601     //                                  tag:'span',
13602     //                                  cls: 'fc-event-time',
13603     //                                  html : cells.length > 1 ? '' : ev.time
13604     //                                },
13605                                     {
13606                                       tag:'span',
13607                                       cls: 'fc-event-title',
13608                                       html : String.format('{0}', ev.title)
13609                                     }
13610
13611
13612                                 ]
13613                             },
13614                             {
13615                                 cls: 'ui-resizable-handle ui-resizable-e',
13616                                 html : '&nbsp;&nbsp;&nbsp'
13617                             }
13618
13619                         ]
13620                     };
13621
13622                     if (i == 0) {
13623                         cfg.cls += ' fc-event-start';
13624                     }
13625                     if ((i+1) == rows.length) {
13626                         cfg.cls += ' fc-event-end';
13627                     }
13628
13629                     var ctr = _this.el.select('.fc-event-container',true).first();
13630                     var cg = ctr.createChild(cfg);
13631
13632                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
13633                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
13634
13635                     var r = (c.more.length) ? 1 : 0;
13636                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
13637                     cg.setWidth(ebox.right - sbox.x -2);
13638
13639                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
13640                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
13641                     cg.on('click', _this.onEventClick, _this, ev);
13642
13643                     ev.els.push(cg);
13644                     
13645                 }
13646                 
13647             }
13648             
13649             
13650             if(c.more.length){
13651                 var  cfg = {
13652                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
13653                     style : 'position: absolute',
13654                     unselectable : "on",
13655                     cn : [
13656                         {
13657                             cls: 'fc-event-inner',
13658                             cn : [
13659                                 {
13660                                   tag:'span',
13661                                   cls: 'fc-event-title',
13662                                   html : 'More'
13663                                 }
13664
13665
13666                             ]
13667                         },
13668                         {
13669                             cls: 'ui-resizable-handle ui-resizable-e',
13670                             html : '&nbsp;&nbsp;&nbsp'
13671                         }
13672
13673                     ]
13674                 };
13675
13676                 var ctr = _this.el.select('.fc-event-container',true).first();
13677                 var cg = ctr.createChild(cfg);
13678
13679                 var sbox = c.select('.fc-day-content',true).first().getBox();
13680                 var ebox = c.select('.fc-day-content',true).first().getBox();
13681                 //Roo.log(cg);
13682                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
13683                 cg.setWidth(ebox.right - sbox.x -2);
13684
13685                 cg.on('click', _this.onMoreEventClick, _this, c.more);
13686                 
13687             }
13688             
13689         });
13690         
13691         
13692         
13693     },
13694     
13695     onEventEnter: function (e, el,event,d) {
13696         this.fireEvent('evententer', this, el, event);
13697     },
13698     
13699     onEventLeave: function (e, el,event,d) {
13700         this.fireEvent('eventleave', this, el, event);
13701     },
13702     
13703     onEventClick: function (e, el,event,d) {
13704         this.fireEvent('eventclick', this, el, event);
13705     },
13706     
13707     onMonthChange: function () {
13708         this.store.load();
13709     },
13710     
13711     onMoreEventClick: function(e, el, more)
13712     {
13713         var _this = this;
13714         
13715         this.calpopover.placement = 'right';
13716         this.calpopover.setTitle('More');
13717         
13718         this.calpopover.setContent('');
13719         
13720         var ctr = this.calpopover.el.select('.popover-content', true).first();
13721         
13722         Roo.each(more, function(m){
13723             var cfg = {
13724                 cls : 'fc-event-hori fc-event-draggable',
13725                 html : m.title
13726             }
13727             var cg = ctr.createChild(cfg);
13728             
13729             cg.on('click', _this.onEventClick, _this, m);
13730         });
13731         
13732         this.calpopover.show(el);
13733         
13734         
13735     },
13736     
13737     onLoad: function () 
13738     {   
13739         this.calevents = [];
13740         var cal = this;
13741         
13742         if(this.store.getCount() > 0){
13743             this.store.data.each(function(d){
13744                cal.addItem({
13745                     id : d.data.id,
13746                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
13747                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
13748                     time : d.data.start_time,
13749                     title : d.data.title,
13750                     description : d.data.description,
13751                     venue : d.data.venue
13752                 });
13753             });
13754         }
13755         
13756         this.renderEvents();
13757         
13758         if(this.calevents.length && this.loadMask){
13759             this.maskEl.hide();
13760         }
13761     },
13762     
13763     onBeforeLoad: function()
13764     {
13765         this.clearEvents();
13766         if(this.loadMask){
13767             this.maskEl.show();
13768         }
13769     }
13770 });
13771
13772  
13773  /*
13774  * - LGPL
13775  *
13776  * element
13777  * 
13778  */
13779
13780 /**
13781  * @class Roo.bootstrap.Popover
13782  * @extends Roo.bootstrap.Component
13783  * Bootstrap Popover class
13784  * @cfg {String} html contents of the popover   (or false to use children..)
13785  * @cfg {String} title of popover (or false to hide)
13786  * @cfg {String} placement how it is placed
13787  * @cfg {String} trigger click || hover (or false to trigger manually)
13788  * @cfg {String} over what (parent or false to trigger manually.)
13789  * @cfg {Number} delay - delay before showing
13790  
13791  * @constructor
13792  * Create a new Popover
13793  * @param {Object} config The config object
13794  */
13795
13796 Roo.bootstrap.Popover = function(config){
13797     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
13798 };
13799
13800 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
13801     
13802     title: 'Fill in a title',
13803     html: false,
13804     
13805     placement : 'right',
13806     trigger : 'hover', // hover
13807     
13808     delay : 0,
13809     
13810     over: 'parent',
13811     
13812     can_build_overlaid : false,
13813     
13814     getChildContainer : function()
13815     {
13816         return this.el.select('.popover-content',true).first();
13817     },
13818     
13819     getAutoCreate : function(){
13820          Roo.log('make popover?');
13821         var cfg = {
13822            cls : 'popover roo-dynamic',
13823            style: 'display:block',
13824            cn : [
13825                 {
13826                     cls : 'arrow'
13827                 },
13828                 {
13829                     cls : 'popover-inner',
13830                     cn : [
13831                         {
13832                             tag: 'h3',
13833                             cls: 'popover-title',
13834                             html : this.title
13835                         },
13836                         {
13837                             cls : 'popover-content',
13838                             html : this.html
13839                         }
13840                     ]
13841                     
13842                 }
13843            ]
13844         };
13845         
13846         return cfg;
13847     },
13848     setTitle: function(str)
13849     {
13850         this.el.select('.popover-title',true).first().dom.innerHTML = str;
13851     },
13852     setContent: function(str)
13853     {
13854         this.el.select('.popover-content',true).first().dom.innerHTML = str;
13855     },
13856     // as it get's added to the bottom of the page.
13857     onRender : function(ct, position)
13858     {
13859         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
13860         if(!this.el){
13861             var cfg = Roo.apply({},  this.getAutoCreate());
13862             cfg.id = Roo.id();
13863             
13864             if (this.cls) {
13865                 cfg.cls += ' ' + this.cls;
13866             }
13867             if (this.style) {
13868                 cfg.style = this.style;
13869             }
13870             Roo.log("adding to ")
13871             this.el = Roo.get(document.body).createChild(cfg, position);
13872             Roo.log(this.el);
13873         }
13874         this.initEvents();
13875     },
13876     
13877     initEvents : function()
13878     {
13879         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
13880         this.el.enableDisplayMode('block');
13881         this.el.hide();
13882         if (this.over === false) {
13883             return; 
13884         }
13885         if (this.triggers === false) {
13886             return;
13887         }
13888         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13889         var triggers = this.trigger ? this.trigger.split(' ') : [];
13890         Roo.each(triggers, function(trigger) {
13891         
13892             if (trigger == 'click') {
13893                 on_el.on('click', this.toggle, this);
13894             } else if (trigger != 'manual') {
13895                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
13896                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
13897       
13898                 on_el.on(eventIn  ,this.enter, this);
13899                 on_el.on(eventOut, this.leave, this);
13900             }
13901         }, this);
13902         
13903     },
13904     
13905     
13906     // private
13907     timeout : null,
13908     hoverState : null,
13909     
13910     toggle : function () {
13911         this.hoverState == 'in' ? this.leave() : this.enter();
13912     },
13913     
13914     enter : function () {
13915        
13916     
13917         clearTimeout(this.timeout);
13918     
13919         this.hoverState = 'in'
13920     
13921         if (!this.delay || !this.delay.show) {
13922             this.show();
13923             return 
13924         }
13925         var _t = this;
13926         this.timeout = setTimeout(function () {
13927             if (_t.hoverState == 'in') {
13928                 _t.show();
13929             }
13930         }, this.delay.show)
13931     },
13932     leave : function() {
13933         clearTimeout(this.timeout);
13934     
13935         this.hoverState = 'out'
13936     
13937         if (!this.delay || !this.delay.hide) {
13938             this.hide();
13939             return 
13940         }
13941         var _t = this;
13942         this.timeout = setTimeout(function () {
13943             if (_t.hoverState == 'out') {
13944                 _t.hide();
13945             }
13946         }, this.delay.hide)
13947     },
13948     
13949     show : function (on_el)
13950     {
13951         if (!on_el) {
13952             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13953         }
13954         // set content.
13955         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
13956         if (this.html !== false) {
13957             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
13958         }
13959         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
13960         if (!this.title.length) {
13961             this.el.select('.popover-title',true).hide();
13962         }
13963         
13964         var placement = typeof this.placement == 'function' ?
13965             this.placement.call(this, this.el, on_el) :
13966             this.placement;
13967             
13968         var autoToken = /\s?auto?\s?/i;
13969         var autoPlace = autoToken.test(placement);
13970         if (autoPlace) {
13971             placement = placement.replace(autoToken, '') || 'top';
13972         }
13973         
13974         //this.el.detach()
13975         //this.el.setXY([0,0]);
13976         this.el.show();
13977         this.el.dom.style.display='block';
13978         this.el.addClass(placement);
13979         
13980         //this.el.appendTo(on_el);
13981         
13982         var p = this.getPosition();
13983         var box = this.el.getBox();
13984         
13985         if (autoPlace) {
13986             // fixme..
13987         }
13988         var align = Roo.bootstrap.Popover.alignment[placement]
13989         this.el.alignTo(on_el, align[0],align[1]);
13990         //var arrow = this.el.select('.arrow',true).first();
13991         //arrow.set(align[2], 
13992         
13993         this.el.addClass('in');
13994         this.hoverState = null;
13995         
13996         if (this.el.hasClass('fade')) {
13997             // fade it?
13998         }
13999         
14000     },
14001     hide : function()
14002     {
14003         this.el.setXY([0,0]);
14004         this.el.removeClass('in');
14005         this.el.hide();
14006         
14007     }
14008     
14009 });
14010
14011 Roo.bootstrap.Popover.alignment = {
14012     'left' : ['r-l', [-10,0], 'right'],
14013     'right' : ['l-r', [10,0], 'left'],
14014     'bottom' : ['t-b', [0,10], 'top'],
14015     'top' : [ 'b-t', [0,-10], 'bottom']
14016 };
14017
14018  /*
14019  * - LGPL
14020  *
14021  * Progress
14022  * 
14023  */
14024
14025 /**
14026  * @class Roo.bootstrap.Progress
14027  * @extends Roo.bootstrap.Component
14028  * Bootstrap Progress class
14029  * @cfg {Boolean} striped striped of the progress bar
14030  * @cfg {Boolean} active animated of the progress bar
14031  * 
14032  * 
14033  * @constructor
14034  * Create a new Progress
14035  * @param {Object} config The config object
14036  */
14037
14038 Roo.bootstrap.Progress = function(config){
14039     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
14040 };
14041
14042 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
14043     
14044     striped : false,
14045     active: false,
14046     
14047     getAutoCreate : function(){
14048         var cfg = {
14049             tag: 'div',
14050             cls: 'progress'
14051         };
14052         
14053         
14054         if(this.striped){
14055             cfg.cls += ' progress-striped';
14056         }
14057       
14058         if(this.active){
14059             cfg.cls += ' active';
14060         }
14061         
14062         
14063         return cfg;
14064     }
14065    
14066 });
14067
14068  
14069
14070  /*
14071  * - LGPL
14072  *
14073  * ProgressBar
14074  * 
14075  */
14076
14077 /**
14078  * @class Roo.bootstrap.ProgressBar
14079  * @extends Roo.bootstrap.Component
14080  * Bootstrap ProgressBar class
14081  * @cfg {Number} aria_valuenow aria-value now
14082  * @cfg {Number} aria_valuemin aria-value min
14083  * @cfg {Number} aria_valuemax aria-value max
14084  * @cfg {String} label label for the progress bar
14085  * @cfg {String} panel (success | info | warning | danger )
14086  * @cfg {String} role role of the progress bar
14087  * @cfg {String} sr_only text
14088  * 
14089  * 
14090  * @constructor
14091  * Create a new ProgressBar
14092  * @param {Object} config The config object
14093  */
14094
14095 Roo.bootstrap.ProgressBar = function(config){
14096     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
14097 };
14098
14099 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
14100     
14101     aria_valuenow : 0,
14102     aria_valuemin : 0,
14103     aria_valuemax : 100,
14104     label : false,
14105     panel : false,
14106     role : false,
14107     sr_only: false,
14108     
14109     getAutoCreate : function()
14110     {
14111         
14112         var cfg = {
14113             tag: 'div',
14114             cls: 'progress-bar',
14115             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
14116         };
14117         
14118         if(this.sr_only){
14119             cfg.cn = {
14120                 tag: 'span',
14121                 cls: 'sr-only',
14122                 html: this.sr_only
14123             }
14124         }
14125         
14126         if(this.role){
14127             cfg.role = this.role;
14128         }
14129         
14130         if(this.aria_valuenow){
14131             cfg['aria-valuenow'] = this.aria_valuenow;
14132         }
14133         
14134         if(this.aria_valuemin){
14135             cfg['aria-valuemin'] = this.aria_valuemin;
14136         }
14137         
14138         if(this.aria_valuemax){
14139             cfg['aria-valuemax'] = this.aria_valuemax;
14140         }
14141         
14142         if(this.label && !this.sr_only){
14143             cfg.html = this.label;
14144         }
14145         
14146         if(this.panel){
14147             cfg.cls += ' progress-bar-' + this.panel;
14148         }
14149         
14150         return cfg;
14151     },
14152     
14153     update : function(aria_valuenow)
14154     {
14155         this.aria_valuenow = aria_valuenow;
14156         
14157         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
14158     }
14159    
14160 });
14161
14162  
14163
14164  /*
14165  * - LGPL
14166  *
14167  * column
14168  * 
14169  */
14170
14171 /**
14172  * @class Roo.bootstrap.TabGroup
14173  * @extends Roo.bootstrap.Column
14174  * Bootstrap Column class
14175  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
14176  * @cfg {Boolean} carousel true to make the group behave like a carousel
14177  * 
14178  * @constructor
14179  * Create a new TabGroup
14180  * @param {Object} config The config object
14181  */
14182
14183 Roo.bootstrap.TabGroup = function(config){
14184     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
14185     if (!this.navId) {
14186         this.navId = Roo.id();
14187     }
14188     this.tabs = [];
14189     Roo.bootstrap.TabGroup.register(this);
14190     
14191 };
14192
14193 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
14194     
14195     carousel : false,
14196     transition : false,
14197      
14198     getAutoCreate : function()
14199     {
14200         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
14201         
14202         cfg.cls += ' tab-content';
14203         
14204         if (this.carousel) {
14205             cfg.cls += ' carousel slide';
14206             cfg.cn = [{
14207                cls : 'carousel-inner'
14208             }]
14209         }
14210         
14211         
14212         return cfg;
14213     },
14214     getChildContainer : function()
14215     {
14216         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
14217     },
14218     
14219     /**
14220     * register a Navigation item
14221     * @param {Roo.bootstrap.NavItem} the navitem to add
14222     */
14223     register : function(item)
14224     {
14225         this.tabs.push( item);
14226         item.navId = this.navId; // not really needed..
14227     
14228     },
14229     
14230     getActivePanel : function()
14231     {
14232         var r = false;
14233         Roo.each(this.tabs, function(t) {
14234             if (t.active) {
14235                 r = t;
14236                 return false;
14237             }
14238             return null;
14239         });
14240         return r;
14241         
14242     },
14243     getPanelByName : function(n)
14244     {
14245         var r = false;
14246         Roo.each(this.tabs, function(t) {
14247             if (t.tabId == n) {
14248                 r = t;
14249                 return false;
14250             }
14251             return null;
14252         });
14253         return r;
14254     },
14255     indexOfPanel : function(p)
14256     {
14257         var r = false;
14258         Roo.each(this.tabs, function(t,i) {
14259             if (t.tabId == p.tabId) {
14260                 r = i;
14261                 return false;
14262             }
14263             return null;
14264         });
14265         return r;
14266     },
14267     /**
14268      * show a specific panel
14269      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
14270      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
14271      */
14272     showPanel : function (pan)
14273     {
14274         
14275         if (typeof(pan) == 'number') {
14276             pan = this.tabs[pan];
14277         }
14278         if (typeof(pan) == 'string') {
14279             pan = this.getPanelByName(pan);
14280         }
14281         if (pan.tabId == this.getActivePanel().tabId) {
14282             return true;
14283         }
14284         var cur = this.getActivePanel();
14285         
14286         if (false === cur.fireEvent('beforedeactivate')) {
14287             return false;
14288         }
14289         
14290         if (this.carousel) {
14291             this.transition = true;
14292             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
14293             var lr = dir == 'next' ? 'left' : 'right';
14294             pan.el.addClass(dir); // or prev
14295             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
14296             cur.el.addClass(lr); // or right
14297             pan.el.addClass(lr);
14298             
14299             var _this = this;
14300             cur.el.on('transitionend', function() {
14301                 Roo.log("trans end?");
14302                 
14303                 pan.el.removeClass([lr,dir]);
14304                 pan.setActive(true);
14305                 
14306                 cur.el.removeClass([lr]);
14307                 cur.setActive(false);
14308                 
14309                 _this.transition = false;
14310                 
14311             }, this, { single:  true } );
14312             return true;
14313         }
14314         
14315         cur.setActive(false);
14316         pan.setActive(true);
14317         return true;
14318         
14319     },
14320     showPanelNext : function()
14321     {
14322         var i = this.indexOfPanel(this.getActivePanel());
14323         if (i > this.tabs.length) {
14324             return;
14325         }
14326         this.showPanel(this.tabs[i+1]);
14327     },
14328     showPanelPrev : function()
14329     {
14330         var i = this.indexOfPanel(this.getActivePanel());
14331         if (i  < 1) {
14332             return;
14333         }
14334         this.showPanel(this.tabs[i-1]);
14335     }
14336     
14337     
14338   
14339 });
14340
14341  
14342
14343  
14344  
14345 Roo.apply(Roo.bootstrap.TabGroup, {
14346     
14347     groups: {},
14348      /**
14349     * register a Navigation Group
14350     * @param {Roo.bootstrap.NavGroup} the navgroup to add
14351     */
14352     register : function(navgrp)
14353     {
14354         this.groups[navgrp.navId] = navgrp;
14355         
14356     },
14357     /**
14358     * fetch a Navigation Group based on the navigation ID
14359     * if one does not exist , it will get created.
14360     * @param {string} the navgroup to add
14361     * @returns {Roo.bootstrap.NavGroup} the navgroup 
14362     */
14363     get: function(navId) {
14364         if (typeof(this.groups[navId]) == 'undefined') {
14365             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
14366         }
14367         return this.groups[navId] ;
14368     }
14369     
14370     
14371     
14372 });
14373
14374  /*
14375  * - LGPL
14376  *
14377  * TabPanel
14378  * 
14379  */
14380
14381 /**
14382  * @class Roo.bootstrap.TabPanel
14383  * @extends Roo.bootstrap.Component
14384  * Bootstrap TabPanel class
14385  * @cfg {Boolean} active panel active
14386  * @cfg {String} html panel content
14387  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
14388  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
14389  * 
14390  * 
14391  * @constructor
14392  * Create a new TabPanel
14393  * @param {Object} config The config object
14394  */
14395
14396 Roo.bootstrap.TabPanel = function(config){
14397     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
14398     this.addEvents({
14399         /**
14400              * @event changed
14401              * Fires when the active status changes
14402              * @param {Roo.bootstrap.TabPanel} this
14403              * @param {Boolean} state the new state
14404             
14405          */
14406         'changed': true,
14407         /**
14408              * @event beforedeactivate
14409              * Fires before a tab is de-activated - can be used to do validation on a form.
14410              * @param {Roo.bootstrap.TabPanel} this
14411              * @return {Boolean} false if there is an error
14412             
14413          */
14414         'beforedeactivate': true
14415      });
14416     
14417     this.tabId = this.tabId || Roo.id();
14418   
14419 };
14420
14421 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
14422     
14423     active: false,
14424     html: false,
14425     tabId: false,
14426     navId : false,
14427     
14428     getAutoCreate : function(){
14429         var cfg = {
14430             tag: 'div',
14431             // item is needed for carousel - not sure if it has any effect otherwise
14432             cls: 'tab-pane item',
14433             html: this.html || ''
14434         };
14435         
14436         if(this.active){
14437             cfg.cls += ' active';
14438         }
14439         
14440         if(this.tabId){
14441             cfg.tabId = this.tabId;
14442         }
14443         
14444         
14445         return cfg;
14446     },
14447     
14448     initEvents:  function()
14449     {
14450         Roo.log('-------- init events on tab panel ---------');
14451         
14452         var p = this.parent();
14453         this.navId = this.navId || p.navId;
14454         
14455         if (typeof(this.navId) != 'undefined') {
14456             // not really needed.. but just in case.. parent should be a NavGroup.
14457             var tg = Roo.bootstrap.TabGroup.get(this.navId);
14458             Roo.log(['register', tg, this]);
14459             tg.register(this);
14460         }
14461     },
14462     
14463     
14464     onRender : function(ct, position)
14465     {
14466        // Roo.log("Call onRender: " + this.xtype);
14467         
14468         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
14469         
14470         
14471         
14472         
14473         
14474     },
14475     
14476     setActive: function(state)
14477     {
14478         Roo.log("panel - set active " + this.tabId + "=" + state);
14479         
14480         this.active = state;
14481         if (!state) {
14482             this.el.removeClass('active');
14483             
14484         } else  if (!this.el.hasClass('active')) {
14485             this.el.addClass('active');
14486         }
14487         this.fireEvent('changed', this, state);
14488     }
14489     
14490     
14491 });
14492  
14493
14494  
14495
14496  /*
14497  * - LGPL
14498  *
14499  * DateField
14500  * 
14501  */
14502
14503 /**
14504  * @class Roo.bootstrap.DateField
14505  * @extends Roo.bootstrap.Input
14506  * Bootstrap DateField class
14507  * @cfg {Number} weekStart default 0
14508  * @cfg {String} viewMode default empty, (months|years)
14509  * @cfg {String} minViewMode default empty, (months|years)
14510  * @cfg {Number} startDate default -Infinity
14511  * @cfg {Number} endDate default Infinity
14512  * @cfg {Boolean} todayHighlight default false
14513  * @cfg {Boolean} todayBtn default false
14514  * @cfg {Boolean} calendarWeeks default false
14515  * @cfg {Object} daysOfWeekDisabled default empty
14516  * @cfg {Boolean} singleMode default false (true | false)
14517  * 
14518  * @cfg {Boolean} keyboardNavigation default true
14519  * @cfg {String} language default en
14520  * 
14521  * @constructor
14522  * Create a new DateField
14523  * @param {Object} config The config object
14524  */
14525
14526 Roo.bootstrap.DateField = function(config){
14527     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
14528      this.addEvents({
14529             /**
14530              * @event show
14531              * Fires when this field show.
14532              * @param {Roo.bootstrap.DateField} this
14533              * @param {Mixed} date The date value
14534              */
14535             show : true,
14536             /**
14537              * @event show
14538              * Fires when this field hide.
14539              * @param {Roo.bootstrap.DateField} this
14540              * @param {Mixed} date The date value
14541              */
14542             hide : true,
14543             /**
14544              * @event select
14545              * Fires when select a date.
14546              * @param {Roo.bootstrap.DateField} this
14547              * @param {Mixed} date The date value
14548              */
14549             select : true
14550         });
14551 };
14552
14553 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
14554     
14555     /**
14556      * @cfg {String} format
14557      * The default date format string which can be overriden for localization support.  The format must be
14558      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
14559      */
14560     format : "m/d/y",
14561     /**
14562      * @cfg {String} altFormats
14563      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
14564      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
14565      */
14566     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
14567     
14568     weekStart : 0,
14569     
14570     viewMode : '',
14571     
14572     minViewMode : '',
14573     
14574     todayHighlight : false,
14575     
14576     todayBtn: false,
14577     
14578     language: 'en',
14579     
14580     keyboardNavigation: true,
14581     
14582     calendarWeeks: false,
14583     
14584     startDate: -Infinity,
14585     
14586     endDate: Infinity,
14587     
14588     daysOfWeekDisabled: [],
14589     
14590     _events: [],
14591     
14592     singleMode : false,
14593     
14594     UTCDate: function()
14595     {
14596         return new Date(Date.UTC.apply(Date, arguments));
14597     },
14598     
14599     UTCToday: function()
14600     {
14601         var today = new Date();
14602         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
14603     },
14604     
14605     getDate: function() {
14606             var d = this.getUTCDate();
14607             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
14608     },
14609     
14610     getUTCDate: function() {
14611             return this.date;
14612     },
14613     
14614     setDate: function(d) {
14615             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
14616     },
14617     
14618     setUTCDate: function(d) {
14619             this.date = d;
14620             this.setValue(this.formatDate(this.date));
14621     },
14622         
14623     onRender: function(ct, position)
14624     {
14625         
14626         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
14627         
14628         this.language = this.language || 'en';
14629         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
14630         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
14631         
14632         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
14633         this.format = this.format || 'm/d/y';
14634         this.isInline = false;
14635         this.isInput = true;
14636         this.component = this.el.select('.add-on', true).first() || false;
14637         this.component = (this.component && this.component.length === 0) ? false : this.component;
14638         this.hasInput = this.component && this.inputEL().length;
14639         
14640         if (typeof(this.minViewMode === 'string')) {
14641             switch (this.minViewMode) {
14642                 case 'months':
14643                     this.minViewMode = 1;
14644                     break;
14645                 case 'years':
14646                     this.minViewMode = 2;
14647                     break;
14648                 default:
14649                     this.minViewMode = 0;
14650                     break;
14651             }
14652         }
14653         
14654         if (typeof(this.viewMode === 'string')) {
14655             switch (this.viewMode) {
14656                 case 'months':
14657                     this.viewMode = 1;
14658                     break;
14659                 case 'years':
14660                     this.viewMode = 2;
14661                     break;
14662                 default:
14663                     this.viewMode = 0;
14664                     break;
14665             }
14666         }
14667                 
14668         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
14669         
14670 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
14671         
14672         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14673         
14674         this.picker().on('mousedown', this.onMousedown, this);
14675         this.picker().on('click', this.onClick, this);
14676         
14677         this.picker().addClass('datepicker-dropdown');
14678         
14679         this.startViewMode = this.viewMode;
14680         
14681         if(this.singleMode){
14682             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
14683                 v.setVisibilityMode(Roo.Element.DISPLAY)
14684                 v.hide();
14685             })
14686             
14687             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
14688                 v.setStyle('width', '189px');
14689             });
14690         }
14691         
14692         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
14693             if(!this.calendarWeeks){
14694                 v.remove();
14695                 return;
14696             };
14697             
14698             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
14699             v.attr('colspan', function(i, val){
14700                 return parseInt(val) + 1;
14701             });
14702         })
14703                         
14704         
14705         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
14706         
14707         this.setStartDate(this.startDate);
14708         this.setEndDate(this.endDate);
14709         
14710         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
14711         
14712         this.fillDow();
14713         this.fillMonths();
14714         this.update();
14715         this.showMode();
14716         
14717         if(this.isInline) {
14718             this.show();
14719         }
14720     },
14721     
14722     picker : function()
14723     {
14724         return this.pickerEl;
14725 //        return this.el.select('.datepicker', true).first();
14726     },
14727     
14728     fillDow: function()
14729     {
14730         var dowCnt = this.weekStart;
14731         
14732         var dow = {
14733             tag: 'tr',
14734             cn: [
14735                 
14736             ]
14737         };
14738         
14739         if(this.calendarWeeks){
14740             dow.cn.push({
14741                 tag: 'th',
14742                 cls: 'cw',
14743                 html: '&nbsp;'
14744             })
14745         }
14746         
14747         while (dowCnt < this.weekStart + 7) {
14748             dow.cn.push({
14749                 tag: 'th',
14750                 cls: 'dow',
14751                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
14752             });
14753         }
14754         
14755         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
14756     },
14757     
14758     fillMonths: function()
14759     {    
14760         var i = 0
14761         var months = this.picker().select('>.datepicker-months td', true).first();
14762         
14763         months.dom.innerHTML = '';
14764         
14765         while (i < 12) {
14766             var month = {
14767                 tag: 'span',
14768                 cls: 'month',
14769                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
14770             }
14771             
14772             months.createChild(month);
14773         }
14774         
14775     },
14776     
14777     update: function()
14778     {
14779         this.date = (typeof(this.date) === 'undefined' || ((typeof(this.date) === 'string') && !this.date.length)) ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
14780         
14781         if (this.date < this.startDate) {
14782             this.viewDate = new Date(this.startDate);
14783         } else if (this.date > this.endDate) {
14784             this.viewDate = new Date(this.endDate);
14785         } else {
14786             this.viewDate = new Date(this.date);
14787         }
14788         
14789         this.fill();
14790     },
14791     
14792     fill: function() 
14793     {
14794         var d = new Date(this.viewDate),
14795                 year = d.getUTCFullYear(),
14796                 month = d.getUTCMonth(),
14797                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
14798                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
14799                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
14800                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
14801                 currentDate = this.date && this.date.valueOf(),
14802                 today = this.UTCToday();
14803         
14804         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
14805         
14806 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
14807         
14808 //        this.picker.select('>tfoot th.today').
14809 //                                              .text(dates[this.language].today)
14810 //                                              .toggle(this.todayBtn !== false);
14811     
14812         this.updateNavArrows();
14813         this.fillMonths();
14814                                                 
14815         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
14816         
14817         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
14818          
14819         prevMonth.setUTCDate(day);
14820         
14821         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
14822         
14823         var nextMonth = new Date(prevMonth);
14824         
14825         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
14826         
14827         nextMonth = nextMonth.valueOf();
14828         
14829         var fillMonths = false;
14830         
14831         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
14832         
14833         while(prevMonth.valueOf() < nextMonth) {
14834             var clsName = '';
14835             
14836             if (prevMonth.getUTCDay() === this.weekStart) {
14837                 if(fillMonths){
14838                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
14839                 }
14840                     
14841                 fillMonths = {
14842                     tag: 'tr',
14843                     cn: []
14844                 };
14845                 
14846                 if(this.calendarWeeks){
14847                     // ISO 8601: First week contains first thursday.
14848                     // ISO also states week starts on Monday, but we can be more abstract here.
14849                     var
14850                     // Start of current week: based on weekstart/current date
14851                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
14852                     // Thursday of this week
14853                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
14854                     // First Thursday of year, year from thursday
14855                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
14856                     // Calendar week: ms between thursdays, div ms per day, div 7 days
14857                     calWeek =  (th - yth) / 864e5 / 7 + 1;
14858                     
14859                     fillMonths.cn.push({
14860                         tag: 'td',
14861                         cls: 'cw',
14862                         html: calWeek
14863                     });
14864                 }
14865             }
14866             
14867             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
14868                 clsName += ' old';
14869             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
14870                 clsName += ' new';
14871             }
14872             if (this.todayHighlight &&
14873                 prevMonth.getUTCFullYear() == today.getFullYear() &&
14874                 prevMonth.getUTCMonth() == today.getMonth() &&
14875                 prevMonth.getUTCDate() == today.getDate()) {
14876                 clsName += ' today';
14877             }
14878             
14879             if (currentDate && prevMonth.valueOf() === currentDate) {
14880                 clsName += ' active';
14881             }
14882             
14883             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
14884                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
14885                     clsName += ' disabled';
14886             }
14887             
14888             fillMonths.cn.push({
14889                 tag: 'td',
14890                 cls: 'day ' + clsName,
14891                 html: prevMonth.getDate()
14892             })
14893             
14894             prevMonth.setDate(prevMonth.getDate()+1);
14895         }
14896           
14897         var currentYear = this.date && this.date.getUTCFullYear();
14898         var currentMonth = this.date && this.date.getUTCMonth();
14899         
14900         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
14901         
14902         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
14903             v.removeClass('active');
14904             
14905             if(currentYear === year && k === currentMonth){
14906                 v.addClass('active');
14907             }
14908             
14909             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
14910                 v.addClass('disabled');
14911             }
14912             
14913         });
14914         
14915         
14916         year = parseInt(year/10, 10) * 10;
14917         
14918         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
14919         
14920         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
14921         
14922         year -= 1;
14923         for (var i = -1; i < 11; i++) {
14924             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
14925                 tag: 'span',
14926                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
14927                 html: year
14928             })
14929             
14930             year += 1;
14931         }
14932     },
14933     
14934     showMode: function(dir) 
14935     {
14936         if (dir) {
14937             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
14938         }
14939         
14940         Roo.each(this.picker().select('>div',true).elements, function(v){
14941             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14942             v.hide();
14943         });
14944         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
14945     },
14946     
14947     place: function()
14948     {
14949         if(this.isInline) return;
14950         
14951         this.picker().removeClass(['bottom', 'top']);
14952         
14953         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
14954             /*
14955              * place to the top of element!
14956              *
14957              */
14958             
14959             this.picker().addClass('top');
14960             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
14961             
14962             return;
14963         }
14964         
14965         this.picker().addClass('bottom');
14966         
14967         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
14968     },
14969     
14970     parseDate : function(value)
14971     {
14972         if(!value || value instanceof Date){
14973             return value;
14974         }
14975         var v = Date.parseDate(value, this.format);
14976         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
14977             v = Date.parseDate(value, 'Y-m-d');
14978         }
14979         if(!v && this.altFormats){
14980             if(!this.altFormatsArray){
14981                 this.altFormatsArray = this.altFormats.split("|");
14982             }
14983             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
14984                 v = Date.parseDate(value, this.altFormatsArray[i]);
14985             }
14986         }
14987         return v;
14988     },
14989     
14990     formatDate : function(date, fmt)
14991     {   
14992         return (!date || !(date instanceof Date)) ?
14993         date : date.dateFormat(fmt || this.format);
14994     },
14995     
14996     onFocus : function()
14997     {
14998         Roo.bootstrap.DateField.superclass.onFocus.call(this);
14999         this.show();
15000     },
15001     
15002     onBlur : function()
15003     {
15004         Roo.bootstrap.DateField.superclass.onBlur.call(this);
15005         
15006         var d = this.inputEl().getValue();
15007         
15008         this.setValue(d);
15009                 
15010         this.hide();
15011     },
15012     
15013     show : function()
15014     {
15015         this.picker().show();
15016         this.update();
15017         this.place();
15018         
15019         this.fireEvent('show', this, this.date);
15020     },
15021     
15022     hide : function()
15023     {
15024         if(this.isInline) return;
15025         this.picker().hide();
15026         this.viewMode = this.startViewMode;
15027         this.showMode();
15028         
15029         this.fireEvent('hide', this, this.date);
15030         
15031     },
15032     
15033     onMousedown: function(e)
15034     {
15035         e.stopPropagation();
15036         e.preventDefault();
15037     },
15038     
15039     keyup: function(e)
15040     {
15041         Roo.bootstrap.DateField.superclass.keyup.call(this);
15042         this.update();
15043     },
15044
15045     setValue: function(v)
15046     {
15047         
15048         // v can be a string or a date..
15049         
15050         
15051         var d = new Date(this.parseDate(v) ).clearTime();
15052         
15053         if(isNaN(d.getTime())){
15054             this.date = this.viewDate = '';
15055             Roo.bootstrap.DateField.superclass.setValue.call(this, '');
15056             return;
15057         }
15058         
15059         v = this.formatDate(d);
15060         
15061         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
15062         
15063         this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
15064      
15065         this.update();
15066
15067         this.fireEvent('select', this, this.date);
15068         
15069     },
15070     
15071     getValue: function()
15072     {
15073         return this.formatDate(this.date);
15074     },
15075     
15076     fireKey: function(e)
15077     {
15078         if (!this.picker().isVisible()){
15079             if (e.keyCode == 27) // allow escape to hide and re-show picker
15080                 this.show();
15081             return;
15082         }
15083         
15084         var dateChanged = false,
15085         dir, day, month,
15086         newDate, newViewDate;
15087         
15088         switch(e.keyCode){
15089             case 27: // escape
15090                 this.hide();
15091                 e.preventDefault();
15092                 break;
15093             case 37: // left
15094             case 39: // right
15095                 if (!this.keyboardNavigation) break;
15096                 dir = e.keyCode == 37 ? -1 : 1;
15097                 
15098                 if (e.ctrlKey){
15099                     newDate = this.moveYear(this.date, dir);
15100                     newViewDate = this.moveYear(this.viewDate, dir);
15101                 } else if (e.shiftKey){
15102                     newDate = this.moveMonth(this.date, dir);
15103                     newViewDate = this.moveMonth(this.viewDate, dir);
15104                 } else {
15105                     newDate = new Date(this.date);
15106                     newDate.setUTCDate(this.date.getUTCDate() + dir);
15107                     newViewDate = new Date(this.viewDate);
15108                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
15109                 }
15110                 if (this.dateWithinRange(newDate)){
15111                     this.date = newDate;
15112                     this.viewDate = newViewDate;
15113                     this.setValue(this.formatDate(this.date));
15114 //                    this.update();
15115                     e.preventDefault();
15116                     dateChanged = true;
15117                 }
15118                 break;
15119             case 38: // up
15120             case 40: // down
15121                 if (!this.keyboardNavigation) break;
15122                 dir = e.keyCode == 38 ? -1 : 1;
15123                 if (e.ctrlKey){
15124                     newDate = this.moveYear(this.date, dir);
15125                     newViewDate = this.moveYear(this.viewDate, dir);
15126                 } else if (e.shiftKey){
15127                     newDate = this.moveMonth(this.date, dir);
15128                     newViewDate = this.moveMonth(this.viewDate, dir);
15129                 } else {
15130                     newDate = new Date(this.date);
15131                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
15132                     newViewDate = new Date(this.viewDate);
15133                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
15134                 }
15135                 if (this.dateWithinRange(newDate)){
15136                     this.date = newDate;
15137                     this.viewDate = newViewDate;
15138                     this.setValue(this.formatDate(this.date));
15139 //                    this.update();
15140                     e.preventDefault();
15141                     dateChanged = true;
15142                 }
15143                 break;
15144             case 13: // enter
15145                 this.setValue(this.formatDate(this.date));
15146                 this.hide();
15147                 e.preventDefault();
15148                 break;
15149             case 9: // tab
15150                 this.setValue(this.formatDate(this.date));
15151                 this.hide();
15152                 break;
15153             case 16: // shift
15154             case 17: // ctrl
15155             case 18: // alt
15156                 break;
15157             default :
15158                 this.hide();
15159                 
15160         }
15161     },
15162     
15163     
15164     onClick: function(e) 
15165     {
15166         e.stopPropagation();
15167         e.preventDefault();
15168         
15169         var target = e.getTarget();
15170         
15171         if(target.nodeName.toLowerCase() === 'i'){
15172             target = Roo.get(target).dom.parentNode;
15173         }
15174         
15175         var nodeName = target.nodeName;
15176         var className = target.className;
15177         var html = target.innerHTML;
15178         //Roo.log(nodeName);
15179         
15180         switch(nodeName.toLowerCase()) {
15181             case 'th':
15182                 switch(className) {
15183                     case 'switch':
15184                         this.showMode(1);
15185                         break;
15186                     case 'prev':
15187                     case 'next':
15188                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
15189                         switch(this.viewMode){
15190                                 case 0:
15191                                         this.viewDate = this.moveMonth(this.viewDate, dir);
15192                                         break;
15193                                 case 1:
15194                                 case 2:
15195                                         this.viewDate = this.moveYear(this.viewDate, dir);
15196                                         break;
15197                         }
15198                         this.fill();
15199                         break;
15200                     case 'today':
15201                         var date = new Date();
15202                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
15203 //                        this.fill()
15204                         this.setValue(this.formatDate(this.date));
15205                         
15206                         this.hide();
15207                         break;
15208                 }
15209                 break;
15210             case 'span':
15211                 if (className.indexOf('disabled') < 0) {
15212                     this.viewDate.setUTCDate(1);
15213                     if (className.indexOf('month') > -1) {
15214                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
15215                     } else {
15216                         var year = parseInt(html, 10) || 0;
15217                         this.viewDate.setUTCFullYear(year);
15218                         
15219                     }
15220                     
15221                     if(this.singleMode){
15222                         this.setValue(this.formatDate(this.viewDate));
15223                         this.hide();
15224                         return;
15225                     }
15226                     
15227                     this.showMode(-1);
15228                     this.fill();
15229                 }
15230                 break;
15231                 
15232             case 'td':
15233                 //Roo.log(className);
15234                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
15235                     var day = parseInt(html, 10) || 1;
15236                     var year = this.viewDate.getUTCFullYear(),
15237                         month = this.viewDate.getUTCMonth();
15238
15239                     if (className.indexOf('old') > -1) {
15240                         if(month === 0 ){
15241                             month = 11;
15242                             year -= 1;
15243                         }else{
15244                             month -= 1;
15245                         }
15246                     } else if (className.indexOf('new') > -1) {
15247                         if (month == 11) {
15248                             month = 0;
15249                             year += 1;
15250                         } else {
15251                             month += 1;
15252                         }
15253                     }
15254                     //Roo.log([year,month,day]);
15255                     this.date = this.UTCDate(year, month, day,0,0,0,0);
15256                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
15257 //                    this.fill();
15258                     //Roo.log(this.formatDate(this.date));
15259                     this.setValue(this.formatDate(this.date));
15260                     this.hide();
15261                 }
15262                 break;
15263         }
15264     },
15265     
15266     setStartDate: function(startDate)
15267     {
15268         this.startDate = startDate || -Infinity;
15269         if (this.startDate !== -Infinity) {
15270             this.startDate = this.parseDate(this.startDate);
15271         }
15272         this.update();
15273         this.updateNavArrows();
15274     },
15275
15276     setEndDate: function(endDate)
15277     {
15278         this.endDate = endDate || Infinity;
15279         if (this.endDate !== Infinity) {
15280             this.endDate = this.parseDate(this.endDate);
15281         }
15282         this.update();
15283         this.updateNavArrows();
15284     },
15285     
15286     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
15287     {
15288         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
15289         if (typeof(this.daysOfWeekDisabled) !== 'object') {
15290             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
15291         }
15292         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
15293             return parseInt(d, 10);
15294         });
15295         this.update();
15296         this.updateNavArrows();
15297     },
15298     
15299     updateNavArrows: function() 
15300     {
15301         if(this.singleMode){
15302             return;
15303         }
15304         
15305         var d = new Date(this.viewDate),
15306         year = d.getUTCFullYear(),
15307         month = d.getUTCMonth();
15308         
15309         Roo.each(this.picker().select('.prev', true).elements, function(v){
15310             v.show();
15311             switch (this.viewMode) {
15312                 case 0:
15313
15314                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
15315                         v.hide();
15316                     }
15317                     break;
15318                 case 1:
15319                 case 2:
15320                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
15321                         v.hide();
15322                     }
15323                     break;
15324             }
15325         });
15326         
15327         Roo.each(this.picker().select('.next', true).elements, function(v){
15328             v.show();
15329             switch (this.viewMode) {
15330                 case 0:
15331
15332                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
15333                         v.hide();
15334                     }
15335                     break;
15336                 case 1:
15337                 case 2:
15338                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
15339                         v.hide();
15340                     }
15341                     break;
15342             }
15343         })
15344     },
15345     
15346     moveMonth: function(date, dir)
15347     {
15348         if (!dir) return date;
15349         var new_date = new Date(date.valueOf()),
15350         day = new_date.getUTCDate(),
15351         month = new_date.getUTCMonth(),
15352         mag = Math.abs(dir),
15353         new_month, test;
15354         dir = dir > 0 ? 1 : -1;
15355         if (mag == 1){
15356             test = dir == -1
15357             // If going back one month, make sure month is not current month
15358             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
15359             ? function(){
15360                 return new_date.getUTCMonth() == month;
15361             }
15362             // If going forward one month, make sure month is as expected
15363             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
15364             : function(){
15365                 return new_date.getUTCMonth() != new_month;
15366             };
15367             new_month = month + dir;
15368             new_date.setUTCMonth(new_month);
15369             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
15370             if (new_month < 0 || new_month > 11)
15371                 new_month = (new_month + 12) % 12;
15372         } else {
15373             // For magnitudes >1, move one month at a time...
15374             for (var i=0; i<mag; i++)
15375                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
15376                 new_date = this.moveMonth(new_date, dir);
15377             // ...then reset the day, keeping it in the new month
15378             new_month = new_date.getUTCMonth();
15379             new_date.setUTCDate(day);
15380             test = function(){
15381                 return new_month != new_date.getUTCMonth();
15382             };
15383         }
15384         // Common date-resetting loop -- if date is beyond end of month, make it
15385         // end of month
15386         while (test()){
15387             new_date.setUTCDate(--day);
15388             new_date.setUTCMonth(new_month);
15389         }
15390         return new_date;
15391     },
15392
15393     moveYear: function(date, dir)
15394     {
15395         return this.moveMonth(date, dir*12);
15396     },
15397
15398     dateWithinRange: function(date)
15399     {
15400         return date >= this.startDate && date <= this.endDate;
15401     },
15402
15403     
15404     remove: function() 
15405     {
15406         this.picker().remove();
15407     }
15408    
15409 });
15410
15411 Roo.apply(Roo.bootstrap.DateField,  {
15412     
15413     head : {
15414         tag: 'thead',
15415         cn: [
15416         {
15417             tag: 'tr',
15418             cn: [
15419             {
15420                 tag: 'th',
15421                 cls: 'prev',
15422                 html: '<i class="fa fa-arrow-left"/>'
15423             },
15424             {
15425                 tag: 'th',
15426                 cls: 'switch',
15427                 colspan: '5'
15428             },
15429             {
15430                 tag: 'th',
15431                 cls: 'next',
15432                 html: '<i class="fa fa-arrow-right"/>'
15433             }
15434
15435             ]
15436         }
15437         ]
15438     },
15439     
15440     content : {
15441         tag: 'tbody',
15442         cn: [
15443         {
15444             tag: 'tr',
15445             cn: [
15446             {
15447                 tag: 'td',
15448                 colspan: '7'
15449             }
15450             ]
15451         }
15452         ]
15453     },
15454     
15455     footer : {
15456         tag: 'tfoot',
15457         cn: [
15458         {
15459             tag: 'tr',
15460             cn: [
15461             {
15462                 tag: 'th',
15463                 colspan: '7',
15464                 cls: 'today'
15465             }
15466                     
15467             ]
15468         }
15469         ]
15470     },
15471     
15472     dates:{
15473         en: {
15474             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
15475             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
15476             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
15477             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
15478             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
15479             today: "Today"
15480         }
15481     },
15482     
15483     modes: [
15484     {
15485         clsName: 'days',
15486         navFnc: 'Month',
15487         navStep: 1
15488     },
15489     {
15490         clsName: 'months',
15491         navFnc: 'FullYear',
15492         navStep: 1
15493     },
15494     {
15495         clsName: 'years',
15496         navFnc: 'FullYear',
15497         navStep: 10
15498     }]
15499 });
15500
15501 Roo.apply(Roo.bootstrap.DateField,  {
15502   
15503     template : {
15504         tag: 'div',
15505         cls: 'datepicker dropdown-menu roo-dynamic',
15506         cn: [
15507         {
15508             tag: 'div',
15509             cls: 'datepicker-days',
15510             cn: [
15511             {
15512                 tag: 'table',
15513                 cls: 'table-condensed',
15514                 cn:[
15515                 Roo.bootstrap.DateField.head,
15516                 {
15517                     tag: 'tbody'
15518                 },
15519                 Roo.bootstrap.DateField.footer
15520                 ]
15521             }
15522             ]
15523         },
15524         {
15525             tag: 'div',
15526             cls: 'datepicker-months',
15527             cn: [
15528             {
15529                 tag: 'table',
15530                 cls: 'table-condensed',
15531                 cn:[
15532                 Roo.bootstrap.DateField.head,
15533                 Roo.bootstrap.DateField.content,
15534                 Roo.bootstrap.DateField.footer
15535                 ]
15536             }
15537             ]
15538         },
15539         {
15540             tag: 'div',
15541             cls: 'datepicker-years',
15542             cn: [
15543             {
15544                 tag: 'table',
15545                 cls: 'table-condensed',
15546                 cn:[
15547                 Roo.bootstrap.DateField.head,
15548                 Roo.bootstrap.DateField.content,
15549                 Roo.bootstrap.DateField.footer
15550                 ]
15551             }
15552             ]
15553         }
15554         ]
15555     }
15556 });
15557
15558  
15559
15560  /*
15561  * - LGPL
15562  *
15563  * TimeField
15564  * 
15565  */
15566
15567 /**
15568  * @class Roo.bootstrap.TimeField
15569  * @extends Roo.bootstrap.Input
15570  * Bootstrap DateField class
15571  * 
15572  * 
15573  * @constructor
15574  * Create a new TimeField
15575  * @param {Object} config The config object
15576  */
15577
15578 Roo.bootstrap.TimeField = function(config){
15579     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
15580     this.addEvents({
15581             /**
15582              * @event show
15583              * Fires when this field show.
15584              * @param {Roo.bootstrap.DateField} this
15585              * @param {Mixed} date The date value
15586              */
15587             show : true,
15588             /**
15589              * @event show
15590              * Fires when this field hide.
15591              * @param {Roo.bootstrap.DateField} this
15592              * @param {Mixed} date The date value
15593              */
15594             hide : true,
15595             /**
15596              * @event select
15597              * Fires when select a date.
15598              * @param {Roo.bootstrap.DateField} this
15599              * @param {Mixed} date The date value
15600              */
15601             select : true
15602         });
15603 };
15604
15605 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
15606     
15607     /**
15608      * @cfg {String} format
15609      * The default time format string which can be overriden for localization support.  The format must be
15610      * valid according to {@link Date#parseDate} (defaults to 'H:i').
15611      */
15612     format : "H:i",
15613        
15614     onRender: function(ct, position)
15615     {
15616         
15617         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
15618                 
15619         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
15620         
15621         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15622         
15623         this.pop = this.picker().select('>.datepicker-time',true).first();
15624         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block' 
15625         
15626         this.picker().on('mousedown', this.onMousedown, this);
15627         this.picker().on('click', this.onClick, this);
15628         
15629         this.picker().addClass('datepicker-dropdown');
15630     
15631         this.fillTime();
15632         this.update();
15633             
15634         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
15635         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
15636         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
15637         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
15638         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
15639         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
15640
15641     },
15642     
15643     fireKey: function(e){
15644         if (!this.picker().isVisible()){
15645             if (e.keyCode == 27) // allow escape to hide and re-show picker
15646                 this.show();
15647             return;
15648         }
15649
15650         e.preventDefault();
15651         
15652         switch(e.keyCode){
15653             case 27: // escape
15654                 this.hide();
15655                 break;
15656             case 37: // left
15657             case 39: // right
15658                 this.onTogglePeriod();
15659                 break;
15660             case 38: // up
15661                 this.onIncrementMinutes();
15662                 break;
15663             case 40: // down
15664                 this.onDecrementMinutes();
15665                 break;
15666             case 13: // enter
15667             case 9: // tab
15668                 this.setTime();
15669                 break;
15670         }
15671     },
15672     
15673     onClick: function(e) {
15674         e.stopPropagation();
15675         e.preventDefault();
15676     },
15677     
15678     picker : function()
15679     {
15680         return this.el.select('.datepicker', true).first();
15681     },
15682     
15683     fillTime: function()
15684     {    
15685         var time = this.pop.select('tbody', true).first();
15686         
15687         time.dom.innerHTML = '';
15688         
15689         time.createChild({
15690             tag: 'tr',
15691             cn: [
15692                 {
15693                     tag: 'td',
15694                     cn: [
15695                         {
15696                             tag: 'a',
15697                             href: '#',
15698                             cls: 'btn',
15699                             cn: [
15700                                 {
15701                                     tag: 'span',
15702                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
15703                                 }
15704                             ]
15705                         } 
15706                     ]
15707                 },
15708                 {
15709                     tag: 'td',
15710                     cls: 'separator'
15711                 },
15712                 {
15713                     tag: 'td',
15714                     cn: [
15715                         {
15716                             tag: 'a',
15717                             href: '#',
15718                             cls: 'btn',
15719                             cn: [
15720                                 {
15721                                     tag: 'span',
15722                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
15723                                 }
15724                             ]
15725                         }
15726                     ]
15727                 },
15728                 {
15729                     tag: 'td',
15730                     cls: 'separator'
15731                 }
15732             ]
15733         });
15734         
15735         time.createChild({
15736             tag: 'tr',
15737             cn: [
15738                 {
15739                     tag: 'td',
15740                     cn: [
15741                         {
15742                             tag: 'span',
15743                             cls: 'timepicker-hour',
15744                             html: '00'
15745                         }  
15746                     ]
15747                 },
15748                 {
15749                     tag: 'td',
15750                     cls: 'separator',
15751                     html: ':'
15752                 },
15753                 {
15754                     tag: 'td',
15755                     cn: [
15756                         {
15757                             tag: 'span',
15758                             cls: 'timepicker-minute',
15759                             html: '00'
15760                         }  
15761                     ]
15762                 },
15763                 {
15764                     tag: 'td',
15765                     cls: 'separator'
15766                 },
15767                 {
15768                     tag: 'td',
15769                     cn: [
15770                         {
15771                             tag: 'button',
15772                             type: 'button',
15773                             cls: 'btn btn-primary period',
15774                             html: 'AM'
15775                             
15776                         }
15777                     ]
15778                 }
15779             ]
15780         });
15781         
15782         time.createChild({
15783             tag: 'tr',
15784             cn: [
15785                 {
15786                     tag: 'td',
15787                     cn: [
15788                         {
15789                             tag: 'a',
15790                             href: '#',
15791                             cls: 'btn',
15792                             cn: [
15793                                 {
15794                                     tag: 'span',
15795                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
15796                                 }
15797                             ]
15798                         }
15799                     ]
15800                 },
15801                 {
15802                     tag: 'td',
15803                     cls: 'separator'
15804                 },
15805                 {
15806                     tag: 'td',
15807                     cn: [
15808                         {
15809                             tag: 'a',
15810                             href: '#',
15811                             cls: 'btn',
15812                             cn: [
15813                                 {
15814                                     tag: 'span',
15815                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
15816                                 }
15817                             ]
15818                         }
15819                     ]
15820                 },
15821                 {
15822                     tag: 'td',
15823                     cls: 'separator'
15824                 }
15825             ]
15826         });
15827         
15828     },
15829     
15830     update: function()
15831     {
15832         
15833         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
15834         
15835         this.fill();
15836     },
15837     
15838     fill: function() 
15839     {
15840         var hours = this.time.getHours();
15841         var minutes = this.time.getMinutes();
15842         var period = 'AM';
15843         
15844         if(hours > 11){
15845             period = 'PM';
15846         }
15847         
15848         if(hours == 0){
15849             hours = 12;
15850         }
15851         
15852         
15853         if(hours > 12){
15854             hours = hours - 12;
15855         }
15856         
15857         if(hours < 10){
15858             hours = '0' + hours;
15859         }
15860         
15861         if(minutes < 10){
15862             minutes = '0' + minutes;
15863         }
15864         
15865         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
15866         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
15867         this.pop.select('button', true).first().dom.innerHTML = period;
15868         
15869     },
15870     
15871     place: function()
15872     {   
15873         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
15874         
15875         var cls = ['bottom'];
15876         
15877         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
15878             cls.pop();
15879             cls.push('top');
15880         }
15881         
15882         cls.push('right');
15883         
15884         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
15885             cls.pop();
15886             cls.push('left');
15887         }
15888         
15889         this.picker().addClass(cls.join('-'));
15890         
15891         var _this = this;
15892         
15893         Roo.each(cls, function(c){
15894             if(c == 'bottom'){
15895                 _this.picker().setTop(_this.inputEl().getHeight());
15896                 return;
15897             }
15898             if(c == 'top'){
15899                 _this.picker().setTop(0 - _this.picker().getHeight());
15900                 return;
15901             }
15902             
15903             if(c == 'left'){
15904                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
15905                 return;
15906             }
15907             if(c == 'right'){
15908                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
15909                 return;
15910             }
15911         });
15912         
15913     },
15914   
15915     onFocus : function()
15916     {
15917         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
15918         this.show();
15919     },
15920     
15921     onBlur : function()
15922     {
15923         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
15924         this.hide();
15925     },
15926     
15927     show : function()
15928     {
15929         this.picker().show();
15930         this.pop.show();
15931         this.update();
15932         this.place();
15933         
15934         this.fireEvent('show', this, this.date);
15935     },
15936     
15937     hide : function()
15938     {
15939         this.picker().hide();
15940         this.pop.hide();
15941         
15942         this.fireEvent('hide', this, this.date);
15943     },
15944     
15945     setTime : function()
15946     {
15947         this.hide();
15948         this.setValue(this.time.format(this.format));
15949         
15950         this.fireEvent('select', this, this.date);
15951         
15952         
15953     },
15954     
15955     onMousedown: function(e){
15956         e.stopPropagation();
15957         e.preventDefault();
15958     },
15959     
15960     onIncrementHours: function()
15961     {
15962         Roo.log('onIncrementHours');
15963         this.time = this.time.add(Date.HOUR, 1);
15964         this.update();
15965         
15966     },
15967     
15968     onDecrementHours: function()
15969     {
15970         Roo.log('onDecrementHours');
15971         this.time = this.time.add(Date.HOUR, -1);
15972         this.update();
15973     },
15974     
15975     onIncrementMinutes: function()
15976     {
15977         Roo.log('onIncrementMinutes');
15978         this.time = this.time.add(Date.MINUTE, 1);
15979         this.update();
15980     },
15981     
15982     onDecrementMinutes: function()
15983     {
15984         Roo.log('onDecrementMinutes');
15985         this.time = this.time.add(Date.MINUTE, -1);
15986         this.update();
15987     },
15988     
15989     onTogglePeriod: function()
15990     {
15991         Roo.log('onTogglePeriod');
15992         this.time = this.time.add(Date.HOUR, 12);
15993         this.update();
15994     }
15995     
15996    
15997 });
15998
15999 Roo.apply(Roo.bootstrap.TimeField,  {
16000     
16001     content : {
16002         tag: 'tbody',
16003         cn: [
16004             {
16005                 tag: 'tr',
16006                 cn: [
16007                 {
16008                     tag: 'td',
16009                     colspan: '7'
16010                 }
16011                 ]
16012             }
16013         ]
16014     },
16015     
16016     footer : {
16017         tag: 'tfoot',
16018         cn: [
16019             {
16020                 tag: 'tr',
16021                 cn: [
16022                 {
16023                     tag: 'th',
16024                     colspan: '7',
16025                     cls: '',
16026                     cn: [
16027                         {
16028                             tag: 'button',
16029                             cls: 'btn btn-info ok',
16030                             html: 'OK'
16031                         }
16032                     ]
16033                 }
16034
16035                 ]
16036             }
16037         ]
16038     }
16039 });
16040
16041 Roo.apply(Roo.bootstrap.TimeField,  {
16042   
16043     template : {
16044         tag: 'div',
16045         cls: 'datepicker dropdown-menu',
16046         cn: [
16047             {
16048                 tag: 'div',
16049                 cls: 'datepicker-time',
16050                 cn: [
16051                 {
16052                     tag: 'table',
16053                     cls: 'table-condensed',
16054                     cn:[
16055                     Roo.bootstrap.TimeField.content,
16056                     Roo.bootstrap.TimeField.footer
16057                     ]
16058                 }
16059                 ]
16060             }
16061         ]
16062     }
16063 });
16064
16065  
16066
16067  /*
16068  * - LGPL
16069  *
16070  * CheckBox
16071  * 
16072  */
16073
16074 /**
16075  * @class Roo.bootstrap.CheckBox
16076  * @extends Roo.bootstrap.Input
16077  * Bootstrap CheckBox class
16078  * 
16079  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
16080  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
16081  * @cfg {String} boxLabel The text that appears beside the checkbox
16082  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
16083  * @cfg {Boolean} checked initnal the element
16084  * @cfg {Boolean} inline inline the element (default false)
16085  * 
16086  * @constructor
16087  * Create a new CheckBox
16088  * @param {Object} config The config object
16089  */
16090
16091 Roo.bootstrap.CheckBox = function(config){
16092     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
16093    
16094         this.addEvents({
16095             /**
16096             * @event check
16097             * Fires when the element is checked or unchecked.
16098             * @param {Roo.bootstrap.CheckBox} this This input
16099             * @param {Boolean} checked The new checked value
16100             */
16101            check : true
16102         });
16103 };
16104
16105 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
16106     
16107     inputType: 'checkbox',
16108     inputValue: 1,
16109     valueOff: 0,
16110     boxLabel: false,
16111     checked: false,
16112     weight : false,
16113     inline: false,
16114     
16115     getAutoCreate : function()
16116     {
16117         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
16118         
16119         var id = Roo.id();
16120         
16121         var cfg = {};
16122         
16123         cfg.cls = 'form-group ' + this.inputType //input-group
16124         
16125         if(this.inline){
16126             cfg.cls += ' ' + this.inputType + '-inline';
16127         }
16128         
16129         var input =  {
16130             tag: 'input',
16131             id : id,
16132             type : this.inputType,
16133             value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
16134             cls : 'roo-' + this.inputType, //'form-box',
16135             placeholder : this.placeholder || ''
16136             
16137         };
16138         
16139         if (this.weight) { // Validity check?
16140             cfg.cls += " " + this.inputType + "-" + this.weight;
16141         }
16142         
16143         if (this.disabled) {
16144             input.disabled=true;
16145         }
16146         
16147         if(this.checked){
16148             input.checked = this.checked;
16149         }
16150         
16151         if (this.name) {
16152             input.name = this.name;
16153         }
16154         
16155         if (this.size) {
16156             input.cls += ' input-' + this.size;
16157         }
16158         
16159         var settings=this;
16160         ['xs','sm','md','lg'].map(function(size){
16161             if (settings[size]) {
16162                 cfg.cls += ' col-' + size + '-' + settings[size];
16163             }
16164         });
16165         
16166        
16167         
16168         var inputblock = input;
16169         
16170         
16171         
16172         
16173         if (this.before || this.after) {
16174             
16175             inputblock = {
16176                 cls : 'input-group',
16177                 cn :  [] 
16178             };
16179             if (this.before) {
16180                 inputblock.cn.push({
16181                     tag :'span',
16182                     cls : 'input-group-addon',
16183                     html : this.before
16184                 });
16185             }
16186             inputblock.cn.push(input);
16187             if (this.after) {
16188                 inputblock.cn.push({
16189                     tag :'span',
16190                     cls : 'input-group-addon',
16191                     html : this.after
16192                 });
16193             }
16194             
16195         };
16196         
16197         if (align ==='left' && this.fieldLabel.length) {
16198                 Roo.log("left and has label");
16199                 cfg.cn = [
16200                     
16201                     {
16202                         tag: 'label',
16203                         'for' :  id,
16204                         cls : 'control-label col-md-' + this.labelWidth,
16205                         html : this.fieldLabel
16206                         
16207                     },
16208                     {
16209                         cls : "col-md-" + (12 - this.labelWidth), 
16210                         cn: [
16211                             inputblock
16212                         ]
16213                     }
16214                     
16215                 ];
16216         } else if ( this.fieldLabel.length) {
16217                 Roo.log(" label");
16218                 cfg.cn = [
16219                    
16220                     {
16221                         tag: this.boxLabel ? 'span' : 'label',
16222                         'for': id,
16223                         cls: 'control-label box-input-label',
16224                         //cls : 'input-group-addon',
16225                         html : this.fieldLabel
16226                         
16227                     },
16228                     
16229                     inputblock
16230                     
16231                 ];
16232
16233         } else {
16234             
16235                 Roo.log(" no label && no align");
16236                 cfg.cn = [  inputblock ] ;
16237                 
16238                 
16239         };
16240          if(this.boxLabel){
16241              var boxLabelCfg = {
16242                 tag: 'label',
16243                 //'for': id, // box label is handled by onclick - so no for...
16244                 cls: 'box-label',
16245                 html: this.boxLabel
16246             }
16247             
16248             if(this.tooltip){
16249                 boxLabelCfg.tooltip = this.tooltip;
16250             }
16251              
16252             cfg.cn.push(boxLabelCfg);
16253         }
16254         
16255         
16256        
16257         return cfg;
16258         
16259     },
16260     
16261     /**
16262      * return the real input element.
16263      */
16264     inputEl: function ()
16265     {
16266         return this.el.select('input.roo-' + this.inputType,true).first();
16267     },
16268     
16269     labelEl: function()
16270     {
16271         return this.el.select('label.control-label',true).first();
16272     },
16273     /* depricated... */
16274     
16275     label: function()
16276     {
16277         return this.labelEl();
16278     },
16279     
16280     initEvents : function()
16281     {
16282 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
16283         
16284         this.inputEl().on('click', this.onClick,  this);
16285         if (this.boxLabel) { 
16286             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
16287         }
16288         
16289     },
16290     
16291     onClick : function()
16292     {   
16293         this.setChecked(!this.checked);
16294     },
16295     
16296     setChecked : function(state,suppressEvent)
16297     {
16298         if(this.inputType == 'radio'){
16299             
16300             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
16301                 e.dom.checked = false;
16302             });
16303             
16304             this.inputEl().dom.checked = true;
16305             
16306             if(suppressEvent !== true){
16307                 this.fireEvent('check', this, true);
16308             }
16309             
16310             this.inputEl().dom.value = this.inputValue;
16311             
16312             return;
16313         }
16314         
16315         this.checked = state;
16316         
16317         if(suppressEvent !== true){
16318             this.fireEvent('check', this, state);
16319         }
16320         
16321         this.inputEl().dom.checked = state;
16322         
16323         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
16324         
16325     },
16326     
16327     getValue : function()
16328     {
16329         if(this.inputType == 'radio'){
16330             return this.getGroupValue();
16331         }
16332         
16333         return this.inputEl().getValue();
16334         
16335     },
16336     
16337     getGroupValue : function()
16338     {
16339         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
16340     },
16341     
16342     setValue : function(v,suppressEvent)
16343     {
16344         if(this.inputType == 'radio'){
16345             this.setGroupValue(v, suppressEvent);
16346             return;
16347         }
16348         
16349         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
16350     },
16351     
16352     setGroupValue : function(v, suppressEvent)
16353     {
16354         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
16355             e.dom.checked = false;
16356             
16357             if(e.dom.value == v){
16358                 e.dom.checked = true;
16359             }
16360         });
16361         
16362         if(suppressEvent !== true){
16363             this.fireEvent('check', this, true);
16364         }
16365
16366         return;
16367     }
16368     
16369 });
16370
16371  
16372 /*
16373  * - LGPL
16374  *
16375  * Radio
16376  * 
16377  */
16378
16379 /**
16380  * @class Roo.bootstrap.Radio
16381  * @extends Roo.bootstrap.CheckBox
16382  * Bootstrap Radio class
16383
16384  * @constructor
16385  * Create a new Radio
16386  * @param {Object} config The config object
16387  */
16388
16389 Roo.bootstrap.Radio = function(config){
16390     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
16391    
16392 };
16393
16394 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
16395     
16396     inputType: 'radio',
16397     inputValue: '',
16398     valueOff: '',
16399     
16400     getAutoCreate : function()
16401     {
16402         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
16403         
16404         var id = Roo.id();
16405         
16406         var cfg = {};
16407         
16408         cfg.cls = 'form-group radio' //input-group
16409         
16410         var input =  {
16411             tag: 'input',
16412             id : id,
16413             type : this.inputType,
16414             //value : (!this.checked) ? this.valueOff : this.inputValue,
16415             value : this.inputValue,
16416             cls : 'roo-radio',
16417             placeholder : this.placeholder || ''
16418             
16419         };
16420           if (this.weight) { // Validity check?
16421             cfg.cls += " radio-" + this.weight;
16422         }
16423         if (this.disabled) {
16424             input.disabled=true;
16425         }
16426         
16427         if(this.checked){
16428             input.checked = this.checked;
16429         }
16430         
16431         if (this.name) {
16432             input.name = this.name;
16433         }
16434         
16435         if (this.size) {
16436             input.cls += ' input-' + this.size;
16437         }
16438         
16439         var settings=this;
16440         ['xs','sm','md','lg'].map(function(size){
16441             if (settings[size]) {
16442                 cfg.cls += ' col-' + size + '-' + settings[size];
16443             }
16444         });
16445         
16446         var inputblock = input;
16447         
16448         if (this.before || this.after) {
16449             
16450             inputblock = {
16451                 cls : 'input-group',
16452                 cn :  [] 
16453             };
16454             if (this.before) {
16455                 inputblock.cn.push({
16456                     tag :'span',
16457                     cls : 'input-group-addon',
16458                     html : this.before
16459                 });
16460             }
16461             inputblock.cn.push(input);
16462             if (this.after) {
16463                 inputblock.cn.push({
16464                     tag :'span',
16465                     cls : 'input-group-addon',
16466                     html : this.after
16467                 });
16468             }
16469             
16470         };
16471         
16472         if (align ==='left' && this.fieldLabel.length) {
16473                 Roo.log("left and has label");
16474                 cfg.cn = [
16475                     
16476                     {
16477                         tag: 'label',
16478                         'for' :  id,
16479                         cls : 'control-label col-md-' + this.labelWidth,
16480                         html : this.fieldLabel
16481                         
16482                     },
16483                     {
16484                         cls : "col-md-" + (12 - this.labelWidth), 
16485                         cn: [
16486                             inputblock
16487                         ]
16488                     }
16489                     
16490                 ];
16491         } else if ( this.fieldLabel.length) {
16492                 Roo.log(" label");
16493                  cfg.cn = [
16494                    
16495                     {
16496                         tag: 'label',
16497                         'for': id,
16498                         cls: 'control-label box-input-label',
16499                         //cls : 'input-group-addon',
16500                         html : this.fieldLabel
16501                         
16502                     },
16503                     
16504                     inputblock
16505                     
16506                 ];
16507
16508         } else {
16509             
16510                    Roo.log(" no label && no align");
16511                 cfg.cn = [
16512                     
16513                         inputblock
16514                     
16515                 ];
16516                 
16517                 
16518         };
16519         
16520         if(this.boxLabel){
16521             cfg.cn.push({
16522                 tag: 'label',
16523                 'for': id,
16524                 cls: 'box-label',
16525                 html: this.boxLabel
16526             })
16527         }
16528         
16529         return cfg;
16530         
16531     },
16532     inputEl: function ()
16533     {
16534         return this.el.select('input.roo-radio',true).first();
16535     },
16536     onClick : function()
16537     {   
16538         Roo.log("click");
16539         Roo.log("click");
16540         this.setChecked(true);
16541     },
16542     
16543     setChecked : function(state,suppressEvent)
16544     {
16545         if(state){
16546             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16547                 v.dom.checked = false;
16548             });
16549         }
16550         
16551         this.checked = state;
16552         this.inputEl().dom.checked = state;
16553         
16554         if(suppressEvent !== true){
16555             this.fireEvent('check', this, state);
16556         }
16557         
16558         //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
16559         
16560     },
16561     
16562     getGroupValue : function()
16563     {
16564         var value = ''
16565         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16566             if(v.dom.checked == true){
16567                 value = v.dom.value;
16568             }
16569         });
16570         
16571         return value;
16572     },
16573     
16574     /**
16575      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
16576      * @return {Mixed} value The field value
16577      */
16578     getValue : function(){
16579         return this.getGroupValue();
16580     }
16581     
16582 });
16583
16584  
16585 //<script type="text/javascript">
16586
16587 /*
16588  * Based  Ext JS Library 1.1.1
16589  * Copyright(c) 2006-2007, Ext JS, LLC.
16590  * LGPL
16591  *
16592  */
16593  
16594 /**
16595  * @class Roo.HtmlEditorCore
16596  * @extends Roo.Component
16597  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
16598  *
16599  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
16600  */
16601
16602 Roo.HtmlEditorCore = function(config){
16603     
16604     
16605     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
16606     
16607     
16608     this.addEvents({
16609         /**
16610          * @event initialize
16611          * Fires when the editor is fully initialized (including the iframe)
16612          * @param {Roo.HtmlEditorCore} this
16613          */
16614         initialize: true,
16615         /**
16616          * @event activate
16617          * Fires when the editor is first receives the focus. Any insertion must wait
16618          * until after this event.
16619          * @param {Roo.HtmlEditorCore} this
16620          */
16621         activate: true,
16622          /**
16623          * @event beforesync
16624          * Fires before the textarea is updated with content from the editor iframe. Return false
16625          * to cancel the sync.
16626          * @param {Roo.HtmlEditorCore} this
16627          * @param {String} html
16628          */
16629         beforesync: true,
16630          /**
16631          * @event beforepush
16632          * Fires before the iframe editor is updated with content from the textarea. Return false
16633          * to cancel the push.
16634          * @param {Roo.HtmlEditorCore} this
16635          * @param {String} html
16636          */
16637         beforepush: true,
16638          /**
16639          * @event sync
16640          * Fires when the textarea is updated with content from the editor iframe.
16641          * @param {Roo.HtmlEditorCore} this
16642          * @param {String} html
16643          */
16644         sync: true,
16645          /**
16646          * @event push
16647          * Fires when the iframe editor is updated with content from the textarea.
16648          * @param {Roo.HtmlEditorCore} this
16649          * @param {String} html
16650          */
16651         push: true,
16652         
16653         /**
16654          * @event editorevent
16655          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
16656          * @param {Roo.HtmlEditorCore} this
16657          */
16658         editorevent: true
16659         
16660     });
16661     
16662     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
16663     
16664     // defaults : white / black...
16665     this.applyBlacklists();
16666     
16667     
16668     
16669 };
16670
16671
16672 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
16673
16674
16675      /**
16676      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
16677      */
16678     
16679     owner : false,
16680     
16681      /**
16682      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
16683      *                        Roo.resizable.
16684      */
16685     resizable : false,
16686      /**
16687      * @cfg {Number} height (in pixels)
16688      */   
16689     height: 300,
16690    /**
16691      * @cfg {Number} width (in pixels)
16692      */   
16693     width: 500,
16694     
16695     /**
16696      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
16697      * 
16698      */
16699     stylesheets: false,
16700     
16701     // id of frame..
16702     frameId: false,
16703     
16704     // private properties
16705     validationEvent : false,
16706     deferHeight: true,
16707     initialized : false,
16708     activated : false,
16709     sourceEditMode : false,
16710     onFocus : Roo.emptyFn,
16711     iframePad:3,
16712     hideMode:'offsets',
16713     
16714     clearUp: true,
16715     
16716     // blacklist + whitelisted elements..
16717     black: false,
16718     white: false,
16719      
16720     
16721
16722     /**
16723      * Protected method that will not generally be called directly. It
16724      * is called when the editor initializes the iframe with HTML contents. Override this method if you
16725      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
16726      */
16727     getDocMarkup : function(){
16728         // body styles..
16729         var st = '';
16730         
16731         // inherit styels from page...?? 
16732         if (this.stylesheets === false) {
16733             
16734             Roo.get(document.head).select('style').each(function(node) {
16735                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16736             });
16737             
16738             Roo.get(document.head).select('link').each(function(node) { 
16739                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16740             });
16741             
16742         } else if (!this.stylesheets.length) {
16743                 // simple..
16744                 st = '<style type="text/css">' +
16745                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16746                    '</style>';
16747         } else { 
16748             
16749         }
16750         
16751         st +=  '<style type="text/css">' +
16752             'IMG { cursor: pointer } ' +
16753         '</style>';
16754
16755         
16756         return '<html><head>' + st  +
16757             //<style type="text/css">' +
16758             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16759             //'</style>' +
16760             ' </head><body class="roo-htmleditor-body"></body></html>';
16761     },
16762
16763     // private
16764     onRender : function(ct, position)
16765     {
16766         var _t = this;
16767         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
16768         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
16769         
16770         
16771         this.el.dom.style.border = '0 none';
16772         this.el.dom.setAttribute('tabIndex', -1);
16773         this.el.addClass('x-hidden hide');
16774         
16775         
16776         
16777         if(Roo.isIE){ // fix IE 1px bogus margin
16778             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
16779         }
16780        
16781         
16782         this.frameId = Roo.id();
16783         
16784          
16785         
16786         var iframe = this.owner.wrap.createChild({
16787             tag: 'iframe',
16788             cls: 'form-control', // bootstrap..
16789             id: this.frameId,
16790             name: this.frameId,
16791             frameBorder : 'no',
16792             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
16793         }, this.el
16794         );
16795         
16796         
16797         this.iframe = iframe.dom;
16798
16799          this.assignDocWin();
16800         
16801         this.doc.designMode = 'on';
16802        
16803         this.doc.open();
16804         this.doc.write(this.getDocMarkup());
16805         this.doc.close();
16806
16807         
16808         var task = { // must defer to wait for browser to be ready
16809             run : function(){
16810                 //console.log("run task?" + this.doc.readyState);
16811                 this.assignDocWin();
16812                 if(this.doc.body || this.doc.readyState == 'complete'){
16813                     try {
16814                         this.doc.designMode="on";
16815                     } catch (e) {
16816                         return;
16817                     }
16818                     Roo.TaskMgr.stop(task);
16819                     this.initEditor.defer(10, this);
16820                 }
16821             },
16822             interval : 10,
16823             duration: 10000,
16824             scope: this
16825         };
16826         Roo.TaskMgr.start(task);
16827
16828     },
16829
16830     // private
16831     onResize : function(w, h)
16832     {
16833          Roo.log('resize: ' +w + ',' + h );
16834         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
16835         if(!this.iframe){
16836             return;
16837         }
16838         if(typeof w == 'number'){
16839             
16840             this.iframe.style.width = w + 'px';
16841         }
16842         if(typeof h == 'number'){
16843             
16844             this.iframe.style.height = h + 'px';
16845             if(this.doc){
16846                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
16847             }
16848         }
16849         
16850     },
16851
16852     /**
16853      * Toggles the editor between standard and source edit mode.
16854      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
16855      */
16856     toggleSourceEdit : function(sourceEditMode){
16857         
16858         this.sourceEditMode = sourceEditMode === true;
16859         
16860         if(this.sourceEditMode){
16861  
16862             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
16863             
16864         }else{
16865             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
16866             //this.iframe.className = '';
16867             this.deferFocus();
16868         }
16869         //this.setSize(this.owner.wrap.getSize());
16870         //this.fireEvent('editmodechange', this, this.sourceEditMode);
16871     },
16872
16873     
16874   
16875
16876     /**
16877      * Protected method that will not generally be called directly. If you need/want
16878      * custom HTML cleanup, this is the method you should override.
16879      * @param {String} html The HTML to be cleaned
16880      * return {String} The cleaned HTML
16881      */
16882     cleanHtml : function(html){
16883         html = String(html);
16884         if(html.length > 5){
16885             if(Roo.isSafari){ // strip safari nonsense
16886                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
16887             }
16888         }
16889         if(html == '&nbsp;'){
16890             html = '';
16891         }
16892         return html;
16893     },
16894
16895     /**
16896      * HTML Editor -> Textarea
16897      * Protected method that will not generally be called directly. Syncs the contents
16898      * of the editor iframe with the textarea.
16899      */
16900     syncValue : function(){
16901         if(this.initialized){
16902             var bd = (this.doc.body || this.doc.documentElement);
16903             //this.cleanUpPaste(); -- this is done else where and causes havoc..
16904             var html = bd.innerHTML;
16905             if(Roo.isSafari){
16906                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
16907                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
16908                 if(m && m[1]){
16909                     html = '<div style="'+m[0]+'">' + html + '</div>';
16910                 }
16911             }
16912             html = this.cleanHtml(html);
16913             // fix up the special chars.. normaly like back quotes in word...
16914             // however we do not want to do this with chinese..
16915             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
16916                 var cc = b.charCodeAt();
16917                 if (
16918                     (cc >= 0x4E00 && cc < 0xA000 ) ||
16919                     (cc >= 0x3400 && cc < 0x4E00 ) ||
16920                     (cc >= 0xf900 && cc < 0xfb00 )
16921                 ) {
16922                         return b;
16923                 }
16924                 return "&#"+cc+";" 
16925             });
16926             if(this.owner.fireEvent('beforesync', this, html) !== false){
16927                 this.el.dom.value = html;
16928                 this.owner.fireEvent('sync', this, html);
16929             }
16930         }
16931     },
16932
16933     /**
16934      * Protected method that will not generally be called directly. Pushes the value of the textarea
16935      * into the iframe editor.
16936      */
16937     pushValue : function(){
16938         if(this.initialized){
16939             var v = this.el.dom.value.trim();
16940             
16941 //            if(v.length < 1){
16942 //                v = '&#160;';
16943 //            }
16944             
16945             if(this.owner.fireEvent('beforepush', this, v) !== false){
16946                 var d = (this.doc.body || this.doc.documentElement);
16947                 d.innerHTML = v;
16948                 this.cleanUpPaste();
16949                 this.el.dom.value = d.innerHTML;
16950                 this.owner.fireEvent('push', this, v);
16951             }
16952         }
16953     },
16954
16955     // private
16956     deferFocus : function(){
16957         this.focus.defer(10, this);
16958     },
16959
16960     // doc'ed in Field
16961     focus : function(){
16962         if(this.win && !this.sourceEditMode){
16963             this.win.focus();
16964         }else{
16965             this.el.focus();
16966         }
16967     },
16968     
16969     assignDocWin: function()
16970     {
16971         var iframe = this.iframe;
16972         
16973          if(Roo.isIE){
16974             this.doc = iframe.contentWindow.document;
16975             this.win = iframe.contentWindow;
16976         } else {
16977 //            if (!Roo.get(this.frameId)) {
16978 //                return;
16979 //            }
16980 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
16981 //            this.win = Roo.get(this.frameId).dom.contentWindow;
16982             
16983             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
16984                 return;
16985             }
16986             
16987             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
16988             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
16989         }
16990     },
16991     
16992     // private
16993     initEditor : function(){
16994         //console.log("INIT EDITOR");
16995         this.assignDocWin();
16996         
16997         
16998         
16999         this.doc.designMode="on";
17000         this.doc.open();
17001         this.doc.write(this.getDocMarkup());
17002         this.doc.close();
17003         
17004         var dbody = (this.doc.body || this.doc.documentElement);
17005         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
17006         // this copies styles from the containing element into thsi one..
17007         // not sure why we need all of this..
17008         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
17009         
17010         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
17011         //ss['background-attachment'] = 'fixed'; // w3c
17012         dbody.bgProperties = 'fixed'; // ie
17013         //Roo.DomHelper.applyStyles(dbody, ss);
17014         Roo.EventManager.on(this.doc, {
17015             //'mousedown': this.onEditorEvent,
17016             'mouseup': this.onEditorEvent,
17017             'dblclick': this.onEditorEvent,
17018             'click': this.onEditorEvent,
17019             'keyup': this.onEditorEvent,
17020             buffer:100,
17021             scope: this
17022         });
17023         if(Roo.isGecko){
17024             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
17025         }
17026         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
17027             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
17028         }
17029         this.initialized = true;
17030
17031         this.owner.fireEvent('initialize', this);
17032         this.pushValue();
17033     },
17034
17035     // private
17036     onDestroy : function(){
17037         
17038         
17039         
17040         if(this.rendered){
17041             
17042             //for (var i =0; i < this.toolbars.length;i++) {
17043             //    // fixme - ask toolbars for heights?
17044             //    this.toolbars[i].onDestroy();
17045            // }
17046             
17047             //this.wrap.dom.innerHTML = '';
17048             //this.wrap.remove();
17049         }
17050     },
17051
17052     // private
17053     onFirstFocus : function(){
17054         
17055         this.assignDocWin();
17056         
17057         
17058         this.activated = true;
17059          
17060     
17061         if(Roo.isGecko){ // prevent silly gecko errors
17062             this.win.focus();
17063             var s = this.win.getSelection();
17064             if(!s.focusNode || s.focusNode.nodeType != 3){
17065                 var r = s.getRangeAt(0);
17066                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
17067                 r.collapse(true);
17068                 this.deferFocus();
17069             }
17070             try{
17071                 this.execCmd('useCSS', true);
17072                 this.execCmd('styleWithCSS', false);
17073             }catch(e){}
17074         }
17075         this.owner.fireEvent('activate', this);
17076     },
17077
17078     // private
17079     adjustFont: function(btn){
17080         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
17081         //if(Roo.isSafari){ // safari
17082         //    adjust *= 2;
17083        // }
17084         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
17085         if(Roo.isSafari){ // safari
17086             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
17087             v =  (v < 10) ? 10 : v;
17088             v =  (v > 48) ? 48 : v;
17089             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
17090             
17091         }
17092         
17093         
17094         v = Math.max(1, v+adjust);
17095         
17096         this.execCmd('FontSize', v  );
17097     },
17098
17099     onEditorEvent : function(e){
17100         this.owner.fireEvent('editorevent', this, e);
17101       //  this.updateToolbar();
17102         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
17103     },
17104
17105     insertTag : function(tg)
17106     {
17107         // could be a bit smarter... -> wrap the current selected tRoo..
17108         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
17109             
17110             range = this.createRange(this.getSelection());
17111             var wrappingNode = this.doc.createElement(tg.toLowerCase());
17112             wrappingNode.appendChild(range.extractContents());
17113             range.insertNode(wrappingNode);
17114
17115             return;
17116             
17117             
17118             
17119         }
17120         this.execCmd("formatblock",   tg);
17121         
17122     },
17123     
17124     insertText : function(txt)
17125     {
17126         
17127         
17128         var range = this.createRange();
17129         range.deleteContents();
17130                //alert(Sender.getAttribute('label'));
17131                
17132         range.insertNode(this.doc.createTextNode(txt));
17133     } ,
17134     
17135      
17136
17137     /**
17138      * Executes a Midas editor command on the editor document and performs necessary focus and
17139      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
17140      * @param {String} cmd The Midas command
17141      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
17142      */
17143     relayCmd : function(cmd, value){
17144         this.win.focus();
17145         this.execCmd(cmd, value);
17146         this.owner.fireEvent('editorevent', this);
17147         //this.updateToolbar();
17148         this.owner.deferFocus();
17149     },
17150
17151     /**
17152      * Executes a Midas editor command directly on the editor document.
17153      * For visual commands, you should use {@link #relayCmd} instead.
17154      * <b>This should only be called after the editor is initialized.</b>
17155      * @param {String} cmd The Midas command
17156      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
17157      */
17158     execCmd : function(cmd, value){
17159         this.doc.execCommand(cmd, false, value === undefined ? null : value);
17160         this.syncValue();
17161     },
17162  
17163  
17164    
17165     /**
17166      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
17167      * to insert tRoo.
17168      * @param {String} text | dom node.. 
17169      */
17170     insertAtCursor : function(text)
17171     {
17172         
17173         
17174         
17175         if(!this.activated){
17176             return;
17177         }
17178         /*
17179         if(Roo.isIE){
17180             this.win.focus();
17181             var r = this.doc.selection.createRange();
17182             if(r){
17183                 r.collapse(true);
17184                 r.pasteHTML(text);
17185                 this.syncValue();
17186                 this.deferFocus();
17187             
17188             }
17189             return;
17190         }
17191         */
17192         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
17193             this.win.focus();
17194             
17195             
17196             // from jquery ui (MIT licenced)
17197             var range, node;
17198             var win = this.win;
17199             
17200             if (win.getSelection && win.getSelection().getRangeAt) {
17201                 range = win.getSelection().getRangeAt(0);
17202                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
17203                 range.insertNode(node);
17204             } else if (win.document.selection && win.document.selection.createRange) {
17205                 // no firefox support
17206                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
17207                 win.document.selection.createRange().pasteHTML(txt);
17208             } else {
17209                 // no firefox support
17210                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
17211                 this.execCmd('InsertHTML', txt);
17212             } 
17213             
17214             this.syncValue();
17215             
17216             this.deferFocus();
17217         }
17218     },
17219  // private
17220     mozKeyPress : function(e){
17221         if(e.ctrlKey){
17222             var c = e.getCharCode(), cmd;
17223           
17224             if(c > 0){
17225                 c = String.fromCharCode(c).toLowerCase();
17226                 switch(c){
17227                     case 'b':
17228                         cmd = 'bold';
17229                         break;
17230                     case 'i':
17231                         cmd = 'italic';
17232                         break;
17233                     
17234                     case 'u':
17235                         cmd = 'underline';
17236                         break;
17237                     
17238                     case 'v':
17239                         this.cleanUpPaste.defer(100, this);
17240                         return;
17241                         
17242                 }
17243                 if(cmd){
17244                     this.win.focus();
17245                     this.execCmd(cmd);
17246                     this.deferFocus();
17247                     e.preventDefault();
17248                 }
17249                 
17250             }
17251         }
17252     },
17253
17254     // private
17255     fixKeys : function(){ // load time branching for fastest keydown performance
17256         if(Roo.isIE){
17257             return function(e){
17258                 var k = e.getKey(), r;
17259                 if(k == e.TAB){
17260                     e.stopEvent();
17261                     r = this.doc.selection.createRange();
17262                     if(r){
17263                         r.collapse(true);
17264                         r.pasteHTML('&#160;&#160;&#160;&#160;');
17265                         this.deferFocus();
17266                     }
17267                     return;
17268                 }
17269                 
17270                 if(k == e.ENTER){
17271                     r = this.doc.selection.createRange();
17272                     if(r){
17273                         var target = r.parentElement();
17274                         if(!target || target.tagName.toLowerCase() != 'li'){
17275                             e.stopEvent();
17276                             r.pasteHTML('<br />');
17277                             r.collapse(false);
17278                             r.select();
17279                         }
17280                     }
17281                 }
17282                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
17283                     this.cleanUpPaste.defer(100, this);
17284                     return;
17285                 }
17286                 
17287                 
17288             };
17289         }else if(Roo.isOpera){
17290             return function(e){
17291                 var k = e.getKey();
17292                 if(k == e.TAB){
17293                     e.stopEvent();
17294                     this.win.focus();
17295                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
17296                     this.deferFocus();
17297                 }
17298                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
17299                     this.cleanUpPaste.defer(100, this);
17300                     return;
17301                 }
17302                 
17303             };
17304         }else if(Roo.isSafari){
17305             return function(e){
17306                 var k = e.getKey();
17307                 
17308                 if(k == e.TAB){
17309                     e.stopEvent();
17310                     this.execCmd('InsertText','\t');
17311                     this.deferFocus();
17312                     return;
17313                 }
17314                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
17315                     this.cleanUpPaste.defer(100, this);
17316                     return;
17317                 }
17318                 
17319              };
17320         }
17321     }(),
17322     
17323     getAllAncestors: function()
17324     {
17325         var p = this.getSelectedNode();
17326         var a = [];
17327         if (!p) {
17328             a.push(p); // push blank onto stack..
17329             p = this.getParentElement();
17330         }
17331         
17332         
17333         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
17334             a.push(p);
17335             p = p.parentNode;
17336         }
17337         a.push(this.doc.body);
17338         return a;
17339     },
17340     lastSel : false,
17341     lastSelNode : false,
17342     
17343     
17344     getSelection : function() 
17345     {
17346         this.assignDocWin();
17347         return Roo.isIE ? this.doc.selection : this.win.getSelection();
17348     },
17349     
17350     getSelectedNode: function() 
17351     {
17352         // this may only work on Gecko!!!
17353         
17354         // should we cache this!!!!
17355         
17356         
17357         
17358          
17359         var range = this.createRange(this.getSelection()).cloneRange();
17360         
17361         if (Roo.isIE) {
17362             var parent = range.parentElement();
17363             while (true) {
17364                 var testRange = range.duplicate();
17365                 testRange.moveToElementText(parent);
17366                 if (testRange.inRange(range)) {
17367                     break;
17368                 }
17369                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
17370                     break;
17371                 }
17372                 parent = parent.parentElement;
17373             }
17374             return parent;
17375         }
17376         
17377         // is ancestor a text element.
17378         var ac =  range.commonAncestorContainer;
17379         if (ac.nodeType == 3) {
17380             ac = ac.parentNode;
17381         }
17382         
17383         var ar = ac.childNodes;
17384          
17385         var nodes = [];
17386         var other_nodes = [];
17387         var has_other_nodes = false;
17388         for (var i=0;i<ar.length;i++) {
17389             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
17390                 continue;
17391             }
17392             // fullly contained node.
17393             
17394             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
17395                 nodes.push(ar[i]);
17396                 continue;
17397             }
17398             
17399             // probably selected..
17400             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
17401                 other_nodes.push(ar[i]);
17402                 continue;
17403             }
17404             // outer..
17405             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
17406                 continue;
17407             }
17408             
17409             
17410             has_other_nodes = true;
17411         }
17412         if (!nodes.length && other_nodes.length) {
17413             nodes= other_nodes;
17414         }
17415         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
17416             return false;
17417         }
17418         
17419         return nodes[0];
17420     },
17421     createRange: function(sel)
17422     {
17423         // this has strange effects when using with 
17424         // top toolbar - not sure if it's a great idea.
17425         //this.editor.contentWindow.focus();
17426         if (typeof sel != "undefined") {
17427             try {
17428                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
17429             } catch(e) {
17430                 return this.doc.createRange();
17431             }
17432         } else {
17433             return this.doc.createRange();
17434         }
17435     },
17436     getParentElement: function()
17437     {
17438         
17439         this.assignDocWin();
17440         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
17441         
17442         var range = this.createRange(sel);
17443          
17444         try {
17445             var p = range.commonAncestorContainer;
17446             while (p.nodeType == 3) { // text node
17447                 p = p.parentNode;
17448             }
17449             return p;
17450         } catch (e) {
17451             return null;
17452         }
17453     
17454     },
17455     /***
17456      *
17457      * Range intersection.. the hard stuff...
17458      *  '-1' = before
17459      *  '0' = hits..
17460      *  '1' = after.
17461      *         [ -- selected range --- ]
17462      *   [fail]                        [fail]
17463      *
17464      *    basically..
17465      *      if end is before start or  hits it. fail.
17466      *      if start is after end or hits it fail.
17467      *
17468      *   if either hits (but other is outside. - then it's not 
17469      *   
17470      *    
17471      **/
17472     
17473     
17474     // @see http://www.thismuchiknow.co.uk/?p=64.
17475     rangeIntersectsNode : function(range, node)
17476     {
17477         var nodeRange = node.ownerDocument.createRange();
17478         try {
17479             nodeRange.selectNode(node);
17480         } catch (e) {
17481             nodeRange.selectNodeContents(node);
17482         }
17483     
17484         var rangeStartRange = range.cloneRange();
17485         rangeStartRange.collapse(true);
17486     
17487         var rangeEndRange = range.cloneRange();
17488         rangeEndRange.collapse(false);
17489     
17490         var nodeStartRange = nodeRange.cloneRange();
17491         nodeStartRange.collapse(true);
17492     
17493         var nodeEndRange = nodeRange.cloneRange();
17494         nodeEndRange.collapse(false);
17495     
17496         return rangeStartRange.compareBoundaryPoints(
17497                  Range.START_TO_START, nodeEndRange) == -1 &&
17498                rangeEndRange.compareBoundaryPoints(
17499                  Range.START_TO_START, nodeStartRange) == 1;
17500         
17501          
17502     },
17503     rangeCompareNode : function(range, node)
17504     {
17505         var nodeRange = node.ownerDocument.createRange();
17506         try {
17507             nodeRange.selectNode(node);
17508         } catch (e) {
17509             nodeRange.selectNodeContents(node);
17510         }
17511         
17512         
17513         range.collapse(true);
17514     
17515         nodeRange.collapse(true);
17516      
17517         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
17518         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
17519          
17520         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
17521         
17522         var nodeIsBefore   =  ss == 1;
17523         var nodeIsAfter    = ee == -1;
17524         
17525         if (nodeIsBefore && nodeIsAfter)
17526             return 0; // outer
17527         if (!nodeIsBefore && nodeIsAfter)
17528             return 1; //right trailed.
17529         
17530         if (nodeIsBefore && !nodeIsAfter)
17531             return 2;  // left trailed.
17532         // fully contined.
17533         return 3;
17534     },
17535
17536     // private? - in a new class?
17537     cleanUpPaste :  function()
17538     {
17539         // cleans up the whole document..
17540         Roo.log('cleanuppaste');
17541         
17542         this.cleanUpChildren(this.doc.body);
17543         var clean = this.cleanWordChars(this.doc.body.innerHTML);
17544         if (clean != this.doc.body.innerHTML) {
17545             this.doc.body.innerHTML = clean;
17546         }
17547         
17548     },
17549     
17550     cleanWordChars : function(input) {// change the chars to hex code
17551         var he = Roo.HtmlEditorCore;
17552         
17553         var output = input;
17554         Roo.each(he.swapCodes, function(sw) { 
17555             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
17556             
17557             output = output.replace(swapper, sw[1]);
17558         });
17559         
17560         return output;
17561     },
17562     
17563     
17564     cleanUpChildren : function (n)
17565     {
17566         if (!n.childNodes.length) {
17567             return;
17568         }
17569         for (var i = n.childNodes.length-1; i > -1 ; i--) {
17570            this.cleanUpChild(n.childNodes[i]);
17571         }
17572     },
17573     
17574     
17575         
17576     
17577     cleanUpChild : function (node)
17578     {
17579         var ed = this;
17580         //console.log(node);
17581         if (node.nodeName == "#text") {
17582             // clean up silly Windows -- stuff?
17583             return; 
17584         }
17585         if (node.nodeName == "#comment") {
17586             node.parentNode.removeChild(node);
17587             // clean up silly Windows -- stuff?
17588             return; 
17589         }
17590         var lcname = node.tagName.toLowerCase();
17591         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
17592         // whitelist of tags..
17593         
17594         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
17595             // remove node.
17596             node.parentNode.removeChild(node);
17597             return;
17598             
17599         }
17600         
17601         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
17602         
17603         // remove <a name=....> as rendering on yahoo mailer is borked with this.
17604         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
17605         
17606         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
17607         //    remove_keep_children = true;
17608         //}
17609         
17610         if (remove_keep_children) {
17611             this.cleanUpChildren(node);
17612             // inserts everything just before this node...
17613             while (node.childNodes.length) {
17614                 var cn = node.childNodes[0];
17615                 node.removeChild(cn);
17616                 node.parentNode.insertBefore(cn, node);
17617             }
17618             node.parentNode.removeChild(node);
17619             return;
17620         }
17621         
17622         if (!node.attributes || !node.attributes.length) {
17623             this.cleanUpChildren(node);
17624             return;
17625         }
17626         
17627         function cleanAttr(n,v)
17628         {
17629             
17630             if (v.match(/^\./) || v.match(/^\//)) {
17631                 return;
17632             }
17633             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
17634                 return;
17635             }
17636             if (v.match(/^#/)) {
17637                 return;
17638             }
17639 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
17640             node.removeAttribute(n);
17641             
17642         }
17643         
17644         var cwhite = this.cwhite;
17645         var cblack = this.cblack;
17646             
17647         function cleanStyle(n,v)
17648         {
17649             if (v.match(/expression/)) { //XSS?? should we even bother..
17650                 node.removeAttribute(n);
17651                 return;
17652             }
17653             
17654             var parts = v.split(/;/);
17655             var clean = [];
17656             
17657             Roo.each(parts, function(p) {
17658                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
17659                 if (!p.length) {
17660                     return true;
17661                 }
17662                 var l = p.split(':').shift().replace(/\s+/g,'');
17663                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
17664                 
17665                 if ( cwhite.length && cblack.indexOf(l) > -1) {
17666 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17667                     //node.removeAttribute(n);
17668                     return true;
17669                 }
17670                 //Roo.log()
17671                 // only allow 'c whitelisted system attributes'
17672                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
17673 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17674                     //node.removeAttribute(n);
17675                     return true;
17676                 }
17677                 
17678                 
17679                  
17680                 
17681                 clean.push(p);
17682                 return true;
17683             });
17684             if (clean.length) { 
17685                 node.setAttribute(n, clean.join(';'));
17686             } else {
17687                 node.removeAttribute(n);
17688             }
17689             
17690         }
17691         
17692         
17693         for (var i = node.attributes.length-1; i > -1 ; i--) {
17694             var a = node.attributes[i];
17695             //console.log(a);
17696             
17697             if (a.name.toLowerCase().substr(0,2)=='on')  {
17698                 node.removeAttribute(a.name);
17699                 continue;
17700             }
17701             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
17702                 node.removeAttribute(a.name);
17703                 continue;
17704             }
17705             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
17706                 cleanAttr(a.name,a.value); // fixme..
17707                 continue;
17708             }
17709             if (a.name == 'style') {
17710                 cleanStyle(a.name,a.value);
17711                 continue;
17712             }
17713             /// clean up MS crap..
17714             // tecnically this should be a list of valid class'es..
17715             
17716             
17717             if (a.name == 'class') {
17718                 if (a.value.match(/^Mso/)) {
17719                     node.className = '';
17720                 }
17721                 
17722                 if (a.value.match(/body/)) {
17723                     node.className = '';
17724                 }
17725                 continue;
17726             }
17727             
17728             // style cleanup!?
17729             // class cleanup?
17730             
17731         }
17732         
17733         
17734         this.cleanUpChildren(node);
17735         
17736         
17737     },
17738     /**
17739      * Clean up MS wordisms...
17740      */
17741     cleanWord : function(node)
17742     {
17743         var _t = this;
17744         var cleanWordChildren = function()
17745         {
17746             if (!node.childNodes.length) {
17747                 return;
17748             }
17749             for (var i = node.childNodes.length-1; i > -1 ; i--) {
17750                _t.cleanWord(node.childNodes[i]);
17751             }
17752         }
17753         
17754         
17755         if (!node) {
17756             this.cleanWord(this.doc.body);
17757             return;
17758         }
17759         if (node.nodeName == "#text") {
17760             // clean up silly Windows -- stuff?
17761             return; 
17762         }
17763         if (node.nodeName == "#comment") {
17764             node.parentNode.removeChild(node);
17765             // clean up silly Windows -- stuff?
17766             return; 
17767         }
17768         
17769         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
17770             node.parentNode.removeChild(node);
17771             return;
17772         }
17773         
17774         // remove - but keep children..
17775         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
17776             while (node.childNodes.length) {
17777                 var cn = node.childNodes[0];
17778                 node.removeChild(cn);
17779                 node.parentNode.insertBefore(cn, node);
17780             }
17781             node.parentNode.removeChild(node);
17782             cleanWordChildren();
17783             return;
17784         }
17785         // clean styles
17786         if (node.className.length) {
17787             
17788             var cn = node.className.split(/\W+/);
17789             var cna = [];
17790             Roo.each(cn, function(cls) {
17791                 if (cls.match(/Mso[a-zA-Z]+/)) {
17792                     return;
17793                 }
17794                 cna.push(cls);
17795             });
17796             node.className = cna.length ? cna.join(' ') : '';
17797             if (!cna.length) {
17798                 node.removeAttribute("class");
17799             }
17800         }
17801         
17802         if (node.hasAttribute("lang")) {
17803             node.removeAttribute("lang");
17804         }
17805         
17806         if (node.hasAttribute("style")) {
17807             
17808             var styles = node.getAttribute("style").split(";");
17809             var nstyle = [];
17810             Roo.each(styles, function(s) {
17811                 if (!s.match(/:/)) {
17812                     return;
17813                 }
17814                 var kv = s.split(":");
17815                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
17816                     return;
17817                 }
17818                 // what ever is left... we allow.
17819                 nstyle.push(s);
17820             });
17821             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
17822             if (!nstyle.length) {
17823                 node.removeAttribute('style');
17824             }
17825         }
17826         
17827         cleanWordChildren();
17828         
17829         
17830     },
17831     domToHTML : function(currentElement, depth, nopadtext) {
17832         
17833         depth = depth || 0;
17834         nopadtext = nopadtext || false;
17835     
17836         if (!currentElement) {
17837             return this.domToHTML(this.doc.body);
17838         }
17839         
17840         //Roo.log(currentElement);
17841         var j;
17842         var allText = false;
17843         var nodeName = currentElement.nodeName;
17844         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
17845         
17846         if  (nodeName == '#text') {
17847             
17848             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
17849         }
17850         
17851         
17852         var ret = '';
17853         if (nodeName != 'BODY') {
17854              
17855             var i = 0;
17856             // Prints the node tagName, such as <A>, <IMG>, etc
17857             if (tagName) {
17858                 var attr = [];
17859                 for(i = 0; i < currentElement.attributes.length;i++) {
17860                     // quoting?
17861                     var aname = currentElement.attributes.item(i).name;
17862                     if (!currentElement.attributes.item(i).value.length) {
17863                         continue;
17864                     }
17865                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
17866                 }
17867                 
17868                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
17869             } 
17870             else {
17871                 
17872                 // eack
17873             }
17874         } else {
17875             tagName = false;
17876         }
17877         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
17878             return ret;
17879         }
17880         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
17881             nopadtext = true;
17882         }
17883         
17884         
17885         // Traverse the tree
17886         i = 0;
17887         var currentElementChild = currentElement.childNodes.item(i);
17888         var allText = true;
17889         var innerHTML  = '';
17890         lastnode = '';
17891         while (currentElementChild) {
17892             // Formatting code (indent the tree so it looks nice on the screen)
17893             var nopad = nopadtext;
17894             if (lastnode == 'SPAN') {
17895                 nopad  = true;
17896             }
17897             // text
17898             if  (currentElementChild.nodeName == '#text') {
17899                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
17900                 toadd = nopadtext ? toadd : toadd.trim();
17901                 if (!nopad && toadd.length > 80) {
17902                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
17903                 }
17904                 innerHTML  += toadd;
17905                 
17906                 i++;
17907                 currentElementChild = currentElement.childNodes.item(i);
17908                 lastNode = '';
17909                 continue;
17910             }
17911             allText = false;
17912             
17913             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
17914                 
17915             // Recursively traverse the tree structure of the child node
17916             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
17917             lastnode = currentElementChild.nodeName;
17918             i++;
17919             currentElementChild=currentElement.childNodes.item(i);
17920         }
17921         
17922         ret += innerHTML;
17923         
17924         if (!allText) {
17925                 // The remaining code is mostly for formatting the tree
17926             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
17927         }
17928         
17929         
17930         if (tagName) {
17931             ret+= "</"+tagName+">";
17932         }
17933         return ret;
17934         
17935     },
17936         
17937     applyBlacklists : function()
17938     {
17939         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
17940         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
17941         
17942         this.white = [];
17943         this.black = [];
17944         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
17945             if (b.indexOf(tag) > -1) {
17946                 return;
17947             }
17948             this.white.push(tag);
17949             
17950         }, this);
17951         
17952         Roo.each(w, function(tag) {
17953             if (b.indexOf(tag) > -1) {
17954                 return;
17955             }
17956             if (this.white.indexOf(tag) > -1) {
17957                 return;
17958             }
17959             this.white.push(tag);
17960             
17961         }, this);
17962         
17963         
17964         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
17965             if (w.indexOf(tag) > -1) {
17966                 return;
17967             }
17968             this.black.push(tag);
17969             
17970         }, this);
17971         
17972         Roo.each(b, function(tag) {
17973             if (w.indexOf(tag) > -1) {
17974                 return;
17975             }
17976             if (this.black.indexOf(tag) > -1) {
17977                 return;
17978             }
17979             this.black.push(tag);
17980             
17981         }, this);
17982         
17983         
17984         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
17985         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
17986         
17987         this.cwhite = [];
17988         this.cblack = [];
17989         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
17990             if (b.indexOf(tag) > -1) {
17991                 return;
17992             }
17993             this.cwhite.push(tag);
17994             
17995         }, this);
17996         
17997         Roo.each(w, function(tag) {
17998             if (b.indexOf(tag) > -1) {
17999                 return;
18000             }
18001             if (this.cwhite.indexOf(tag) > -1) {
18002                 return;
18003             }
18004             this.cwhite.push(tag);
18005             
18006         }, this);
18007         
18008         
18009         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
18010             if (w.indexOf(tag) > -1) {
18011                 return;
18012             }
18013             this.cblack.push(tag);
18014             
18015         }, this);
18016         
18017         Roo.each(b, function(tag) {
18018             if (w.indexOf(tag) > -1) {
18019                 return;
18020             }
18021             if (this.cblack.indexOf(tag) > -1) {
18022                 return;
18023             }
18024             this.cblack.push(tag);
18025             
18026         }, this);
18027     },
18028     
18029     setStylesheets : function(stylesheets)
18030     {
18031         if(typeof(stylesheets) == 'string'){
18032             Roo.get(this.iframe.contentDocument.head).createChild({
18033                 tag : 'link',
18034                 rel : 'stylesheet',
18035                 type : 'text/css',
18036                 href : stylesheets
18037             });
18038             
18039             return;
18040         }
18041         var _this = this;
18042      
18043         Roo.each(stylesheets, function(s) {
18044             if(!s.length){
18045                 return;
18046             }
18047             
18048             Roo.get(_this.iframe.contentDocument.head).createChild({
18049                 tag : 'link',
18050                 rel : 'stylesheet',
18051                 type : 'text/css',
18052                 href : s
18053             });
18054         });
18055
18056         
18057     },
18058     
18059     removeStylesheets : function()
18060     {
18061         var _this = this;
18062         
18063         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
18064             s.remove();
18065         });
18066     }
18067     
18068     // hide stuff that is not compatible
18069     /**
18070      * @event blur
18071      * @hide
18072      */
18073     /**
18074      * @event change
18075      * @hide
18076      */
18077     /**
18078      * @event focus
18079      * @hide
18080      */
18081     /**
18082      * @event specialkey
18083      * @hide
18084      */
18085     /**
18086      * @cfg {String} fieldClass @hide
18087      */
18088     /**
18089      * @cfg {String} focusClass @hide
18090      */
18091     /**
18092      * @cfg {String} autoCreate @hide
18093      */
18094     /**
18095      * @cfg {String} inputType @hide
18096      */
18097     /**
18098      * @cfg {String} invalidClass @hide
18099      */
18100     /**
18101      * @cfg {String} invalidText @hide
18102      */
18103     /**
18104      * @cfg {String} msgFx @hide
18105      */
18106     /**
18107      * @cfg {String} validateOnBlur @hide
18108      */
18109 });
18110
18111 Roo.HtmlEditorCore.white = [
18112         'area', 'br', 'img', 'input', 'hr', 'wbr',
18113         
18114        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
18115        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
18116        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
18117        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
18118        'table',   'ul',         'xmp', 
18119        
18120        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
18121       'thead',   'tr', 
18122      
18123       'dir', 'menu', 'ol', 'ul', 'dl',
18124        
18125       'embed',  'object'
18126 ];
18127
18128
18129 Roo.HtmlEditorCore.black = [
18130     //    'embed',  'object', // enable - backend responsiblity to clean thiese
18131         'applet', // 
18132         'base',   'basefont', 'bgsound', 'blink',  'body', 
18133         'frame',  'frameset', 'head',    'html',   'ilayer', 
18134         'iframe', 'layer',  'link',     'meta',    'object',   
18135         'script', 'style' ,'title',  'xml' // clean later..
18136 ];
18137 Roo.HtmlEditorCore.clean = [
18138     'script', 'style', 'title', 'xml'
18139 ];
18140 Roo.HtmlEditorCore.remove = [
18141     'font'
18142 ];
18143 // attributes..
18144
18145 Roo.HtmlEditorCore.ablack = [
18146     'on'
18147 ];
18148     
18149 Roo.HtmlEditorCore.aclean = [ 
18150     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
18151 ];
18152
18153 // protocols..
18154 Roo.HtmlEditorCore.pwhite= [
18155         'http',  'https',  'mailto'
18156 ];
18157
18158 // white listed style attributes.
18159 Roo.HtmlEditorCore.cwhite= [
18160       //  'text-align', /// default is to allow most things..
18161       
18162          
18163 //        'font-size'//??
18164 ];
18165
18166 // black listed style attributes.
18167 Roo.HtmlEditorCore.cblack= [
18168       //  'font-size' -- this can be set by the project 
18169 ];
18170
18171
18172 Roo.HtmlEditorCore.swapCodes   =[ 
18173     [    8211, "--" ], 
18174     [    8212, "--" ], 
18175     [    8216,  "'" ],  
18176     [    8217, "'" ],  
18177     [    8220, '"' ],  
18178     [    8221, '"' ],  
18179     [    8226, "*" ],  
18180     [    8230, "..." ]
18181 ]; 
18182
18183     /*
18184  * - LGPL
18185  *
18186  * HtmlEditor
18187  * 
18188  */
18189
18190 /**
18191  * @class Roo.bootstrap.HtmlEditor
18192  * @extends Roo.bootstrap.TextArea
18193  * Bootstrap HtmlEditor class
18194
18195  * @constructor
18196  * Create a new HtmlEditor
18197  * @param {Object} config The config object
18198  */
18199
18200 Roo.bootstrap.HtmlEditor = function(config){
18201     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
18202     if (!this.toolbars) {
18203         this.toolbars = [];
18204     }
18205     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
18206     this.addEvents({
18207             /**
18208              * @event initialize
18209              * Fires when the editor is fully initialized (including the iframe)
18210              * @param {HtmlEditor} this
18211              */
18212             initialize: true,
18213             /**
18214              * @event activate
18215              * Fires when the editor is first receives the focus. Any insertion must wait
18216              * until after this event.
18217              * @param {HtmlEditor} this
18218              */
18219             activate: true,
18220              /**
18221              * @event beforesync
18222              * Fires before the textarea is updated with content from the editor iframe. Return false
18223              * to cancel the sync.
18224              * @param {HtmlEditor} this
18225              * @param {String} html
18226              */
18227             beforesync: true,
18228              /**
18229              * @event beforepush
18230              * Fires before the iframe editor is updated with content from the textarea. Return false
18231              * to cancel the push.
18232              * @param {HtmlEditor} this
18233              * @param {String} html
18234              */
18235             beforepush: true,
18236              /**
18237              * @event sync
18238              * Fires when the textarea is updated with content from the editor iframe.
18239              * @param {HtmlEditor} this
18240              * @param {String} html
18241              */
18242             sync: true,
18243              /**
18244              * @event push
18245              * Fires when the iframe editor is updated with content from the textarea.
18246              * @param {HtmlEditor} this
18247              * @param {String} html
18248              */
18249             push: true,
18250              /**
18251              * @event editmodechange
18252              * Fires when the editor switches edit modes
18253              * @param {HtmlEditor} this
18254              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
18255              */
18256             editmodechange: true,
18257             /**
18258              * @event editorevent
18259              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
18260              * @param {HtmlEditor} this
18261              */
18262             editorevent: true,
18263             /**
18264              * @event firstfocus
18265              * Fires when on first focus - needed by toolbars..
18266              * @param {HtmlEditor} this
18267              */
18268             firstfocus: true,
18269             /**
18270              * @event autosave
18271              * Auto save the htmlEditor value as a file into Events
18272              * @param {HtmlEditor} this
18273              */
18274             autosave: true,
18275             /**
18276              * @event savedpreview
18277              * preview the saved version of htmlEditor
18278              * @param {HtmlEditor} this
18279              */
18280             savedpreview: true
18281         });
18282 };
18283
18284
18285 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
18286     
18287     
18288       /**
18289      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
18290      */
18291     toolbars : false,
18292    
18293      /**
18294      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
18295      *                        Roo.resizable.
18296      */
18297     resizable : false,
18298      /**
18299      * @cfg {Number} height (in pixels)
18300      */   
18301     height: 300,
18302    /**
18303      * @cfg {Number} width (in pixels)
18304      */   
18305     width: false,
18306     
18307     /**
18308      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
18309      * 
18310      */
18311     stylesheets: false,
18312     
18313     // id of frame..
18314     frameId: false,
18315     
18316     // private properties
18317     validationEvent : false,
18318     deferHeight: true,
18319     initialized : false,
18320     activated : false,
18321     
18322     onFocus : Roo.emptyFn,
18323     iframePad:3,
18324     hideMode:'offsets',
18325     
18326     
18327     tbContainer : false,
18328     
18329     toolbarContainer :function() {
18330         return this.wrap.select('.x-html-editor-tb',true).first();
18331     },
18332
18333     /**
18334      * Protected method that will not generally be called directly. It
18335      * is called when the editor creates its toolbar. Override this method if you need to
18336      * add custom toolbar buttons.
18337      * @param {HtmlEditor} editor
18338      */
18339     createToolbar : function(){
18340         
18341         Roo.log("create toolbars");
18342         
18343         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
18344         this.toolbars[0].render(this.toolbarContainer());
18345         
18346         return;
18347         
18348 //        if (!editor.toolbars || !editor.toolbars.length) {
18349 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
18350 //        }
18351 //        
18352 //        for (var i =0 ; i < editor.toolbars.length;i++) {
18353 //            editor.toolbars[i] = Roo.factory(
18354 //                    typeof(editor.toolbars[i]) == 'string' ?
18355 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
18356 //                Roo.bootstrap.HtmlEditor);
18357 //            editor.toolbars[i].init(editor);
18358 //        }
18359     },
18360
18361      
18362     // private
18363     onRender : function(ct, position)
18364     {
18365        // Roo.log("Call onRender: " + this.xtype);
18366         var _t = this;
18367         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
18368       
18369         this.wrap = this.inputEl().wrap({
18370             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
18371         });
18372         
18373         this.editorcore.onRender(ct, position);
18374          
18375         if (this.resizable) {
18376             this.resizeEl = new Roo.Resizable(this.wrap, {
18377                 pinned : true,
18378                 wrap: true,
18379                 dynamic : true,
18380                 minHeight : this.height,
18381                 height: this.height,
18382                 handles : this.resizable,
18383                 width: this.width,
18384                 listeners : {
18385                     resize : function(r, w, h) {
18386                         _t.onResize(w,h); // -something
18387                     }
18388                 }
18389             });
18390             
18391         }
18392         this.createToolbar(this);
18393        
18394         
18395         if(!this.width && this.resizable){
18396             this.setSize(this.wrap.getSize());
18397         }
18398         if (this.resizeEl) {
18399             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
18400             // should trigger onReize..
18401         }
18402         
18403     },
18404
18405     // private
18406     onResize : function(w, h)
18407     {
18408         Roo.log('resize: ' +w + ',' + h );
18409         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
18410         var ew = false;
18411         var eh = false;
18412         
18413         if(this.inputEl() ){
18414             if(typeof w == 'number'){
18415                 var aw = w - this.wrap.getFrameWidth('lr');
18416                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
18417                 ew = aw;
18418             }
18419             if(typeof h == 'number'){
18420                  var tbh = -11;  // fixme it needs to tool bar size!
18421                 for (var i =0; i < this.toolbars.length;i++) {
18422                     // fixme - ask toolbars for heights?
18423                     tbh += this.toolbars[i].el.getHeight();
18424                     //if (this.toolbars[i].footer) {
18425                     //    tbh += this.toolbars[i].footer.el.getHeight();
18426                     //}
18427                 }
18428               
18429                 
18430                 
18431                 
18432                 
18433                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
18434                 ah -= 5; // knock a few pixes off for look..
18435                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
18436                 var eh = ah;
18437             }
18438         }
18439         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
18440         this.editorcore.onResize(ew,eh);
18441         
18442     },
18443
18444     /**
18445      * Toggles the editor between standard and source edit mode.
18446      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
18447      */
18448     toggleSourceEdit : function(sourceEditMode)
18449     {
18450         this.editorcore.toggleSourceEdit(sourceEditMode);
18451         
18452         if(this.editorcore.sourceEditMode){
18453             Roo.log('editor - showing textarea');
18454             
18455 //            Roo.log('in');
18456 //            Roo.log(this.syncValue());
18457             this.syncValue();
18458             this.inputEl().removeClass(['hide', 'x-hidden']);
18459             this.inputEl().dom.removeAttribute('tabIndex');
18460             this.inputEl().focus();
18461         }else{
18462             Roo.log('editor - hiding textarea');
18463 //            Roo.log('out')
18464 //            Roo.log(this.pushValue()); 
18465             this.pushValue();
18466             
18467             this.inputEl().addClass(['hide', 'x-hidden']);
18468             this.inputEl().dom.setAttribute('tabIndex', -1);
18469             //this.deferFocus();
18470         }
18471          
18472         if(this.resizable){
18473             this.setSize(this.wrap.getSize());
18474         }
18475         
18476         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
18477     },
18478  
18479     // private (for BoxComponent)
18480     adjustSize : Roo.BoxComponent.prototype.adjustSize,
18481
18482     // private (for BoxComponent)
18483     getResizeEl : function(){
18484         return this.wrap;
18485     },
18486
18487     // private (for BoxComponent)
18488     getPositionEl : function(){
18489         return this.wrap;
18490     },
18491
18492     // private
18493     initEvents : function(){
18494         this.originalValue = this.getValue();
18495     },
18496
18497 //    /**
18498 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
18499 //     * @method
18500 //     */
18501 //    markInvalid : Roo.emptyFn,
18502 //    /**
18503 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
18504 //     * @method
18505 //     */
18506 //    clearInvalid : Roo.emptyFn,
18507
18508     setValue : function(v){
18509         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
18510         this.editorcore.pushValue();
18511     },
18512
18513      
18514     // private
18515     deferFocus : function(){
18516         this.focus.defer(10, this);
18517     },
18518
18519     // doc'ed in Field
18520     focus : function(){
18521         this.editorcore.focus();
18522         
18523     },
18524       
18525
18526     // private
18527     onDestroy : function(){
18528         
18529         
18530         
18531         if(this.rendered){
18532             
18533             for (var i =0; i < this.toolbars.length;i++) {
18534                 // fixme - ask toolbars for heights?
18535                 this.toolbars[i].onDestroy();
18536             }
18537             
18538             this.wrap.dom.innerHTML = '';
18539             this.wrap.remove();
18540         }
18541     },
18542
18543     // private
18544     onFirstFocus : function(){
18545         //Roo.log("onFirstFocus");
18546         this.editorcore.onFirstFocus();
18547          for (var i =0; i < this.toolbars.length;i++) {
18548             this.toolbars[i].onFirstFocus();
18549         }
18550         
18551     },
18552     
18553     // private
18554     syncValue : function()
18555     {   
18556         this.editorcore.syncValue();
18557     },
18558     
18559     pushValue : function()
18560     {   
18561         this.editorcore.pushValue();
18562     }
18563      
18564     
18565     // hide stuff that is not compatible
18566     /**
18567      * @event blur
18568      * @hide
18569      */
18570     /**
18571      * @event change
18572      * @hide
18573      */
18574     /**
18575      * @event focus
18576      * @hide
18577      */
18578     /**
18579      * @event specialkey
18580      * @hide
18581      */
18582     /**
18583      * @cfg {String} fieldClass @hide
18584      */
18585     /**
18586      * @cfg {String} focusClass @hide
18587      */
18588     /**
18589      * @cfg {String} autoCreate @hide
18590      */
18591     /**
18592      * @cfg {String} inputType @hide
18593      */
18594     /**
18595      * @cfg {String} invalidClass @hide
18596      */
18597     /**
18598      * @cfg {String} invalidText @hide
18599      */
18600     /**
18601      * @cfg {String} msgFx @hide
18602      */
18603     /**
18604      * @cfg {String} validateOnBlur @hide
18605      */
18606 });
18607  
18608     
18609    
18610    
18611    
18612       
18613 Roo.namespace('Roo.bootstrap.htmleditor');
18614 /**
18615  * @class Roo.bootstrap.HtmlEditorToolbar1
18616  * Basic Toolbar
18617  * 
18618  * Usage:
18619  *
18620  new Roo.bootstrap.HtmlEditor({
18621     ....
18622     toolbars : [
18623         new Roo.bootstrap.HtmlEditorToolbar1({
18624             disable : { fonts: 1 , format: 1, ..., ... , ...],
18625             btns : [ .... ]
18626         })
18627     }
18628      
18629  * 
18630  * @cfg {Object} disable List of elements to disable..
18631  * @cfg {Array} btns List of additional buttons.
18632  * 
18633  * 
18634  * NEEDS Extra CSS? 
18635  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
18636  */
18637  
18638 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
18639 {
18640     
18641     Roo.apply(this, config);
18642     
18643     // default disabled, based on 'good practice'..
18644     this.disable = this.disable || {};
18645     Roo.applyIf(this.disable, {
18646         fontSize : true,
18647         colors : true,
18648         specialElements : true
18649     });
18650     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
18651     
18652     this.editor = config.editor;
18653     this.editorcore = config.editor.editorcore;
18654     
18655     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
18656     
18657     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
18658     // dont call parent... till later.
18659 }
18660 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
18661      
18662     bar : true,
18663     
18664     editor : false,
18665     editorcore : false,
18666     
18667     
18668     formats : [
18669         "p" ,  
18670         "h1","h2","h3","h4","h5","h6", 
18671         "pre", "code", 
18672         "abbr", "acronym", "address", "cite", "samp", "var",
18673         'div','span'
18674     ],
18675     
18676     onRender : function(ct, position)
18677     {
18678        // Roo.log("Call onRender: " + this.xtype);
18679         
18680        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
18681        Roo.log(this.el);
18682        this.el.dom.style.marginBottom = '0';
18683        var _this = this;
18684        var editorcore = this.editorcore;
18685        var editor= this.editor;
18686        
18687        var children = [];
18688        var btn = function(id,cmd , toggle, handler){
18689        
18690             var  event = toggle ? 'toggle' : 'click';
18691        
18692             var a = {
18693                 size : 'sm',
18694                 xtype: 'Button',
18695                 xns: Roo.bootstrap,
18696                 glyphicon : id,
18697                 cmd : id || cmd,
18698                 enableToggle:toggle !== false,
18699                 //html : 'submit'
18700                 pressed : toggle ? false : null,
18701                 listeners : {}
18702             }
18703             a.listeners[toggle ? 'toggle' : 'click'] = function() {
18704                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
18705             }
18706             children.push(a);
18707             return a;
18708        }
18709         
18710         var style = {
18711                 xtype: 'Button',
18712                 size : 'sm',
18713                 xns: Roo.bootstrap,
18714                 glyphicon : 'font',
18715                 //html : 'submit'
18716                 menu : {
18717                     xtype: 'Menu',
18718                     xns: Roo.bootstrap,
18719                     items:  []
18720                 }
18721         };
18722         Roo.each(this.formats, function(f) {
18723             style.menu.items.push({
18724                 xtype :'MenuItem',
18725                 xns: Roo.bootstrap,
18726                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
18727                 tagname : f,
18728                 listeners : {
18729                     click : function()
18730                     {
18731                         editorcore.insertTag(this.tagname);
18732                         editor.focus();
18733                     }
18734                 }
18735                 
18736             });
18737         });
18738          children.push(style);   
18739             
18740             
18741         btn('bold',false,true);
18742         btn('italic',false,true);
18743         btn('align-left', 'justifyleft',true);
18744         btn('align-center', 'justifycenter',true);
18745         btn('align-right' , 'justifyright',true);
18746         btn('link', false, false, function(btn) {
18747             //Roo.log("create link?");
18748             var url = prompt(this.createLinkText, this.defaultLinkValue);
18749             if(url && url != 'http:/'+'/'){
18750                 this.editorcore.relayCmd('createlink', url);
18751             }
18752         }),
18753         btn('list','insertunorderedlist',true);
18754         btn('pencil', false,true, function(btn){
18755                 Roo.log(this);
18756                 
18757                 this.toggleSourceEdit(btn.pressed);
18758         });
18759         /*
18760         var cog = {
18761                 xtype: 'Button',
18762                 size : 'sm',
18763                 xns: Roo.bootstrap,
18764                 glyphicon : 'cog',
18765                 //html : 'submit'
18766                 menu : {
18767                     xtype: 'Menu',
18768                     xns: Roo.bootstrap,
18769                     items:  []
18770                 }
18771         };
18772         
18773         cog.menu.items.push({
18774             xtype :'MenuItem',
18775             xns: Roo.bootstrap,
18776             html : Clean styles,
18777             tagname : f,
18778             listeners : {
18779                 click : function()
18780                 {
18781                     editorcore.insertTag(this.tagname);
18782                     editor.focus();
18783                 }
18784             }
18785             
18786         });
18787        */
18788         
18789          
18790        this.xtype = 'NavSimplebar';
18791         
18792         for(var i=0;i< children.length;i++) {
18793             
18794             this.buttons.add(this.addxtypeChild(children[i]));
18795             
18796         }
18797         
18798         editor.on('editorevent', this.updateToolbar, this);
18799     },
18800     onBtnClick : function(id)
18801     {
18802        this.editorcore.relayCmd(id);
18803        this.editorcore.focus();
18804     },
18805     
18806     /**
18807      * Protected method that will not generally be called directly. It triggers
18808      * a toolbar update by reading the markup state of the current selection in the editor.
18809      */
18810     updateToolbar: function(){
18811
18812         if(!this.editorcore.activated){
18813             this.editor.onFirstFocus(); // is this neeed?
18814             return;
18815         }
18816
18817         var btns = this.buttons; 
18818         var doc = this.editorcore.doc;
18819         btns.get('bold').setActive(doc.queryCommandState('bold'));
18820         btns.get('italic').setActive(doc.queryCommandState('italic'));
18821         //btns.get('underline').setActive(doc.queryCommandState('underline'));
18822         
18823         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
18824         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
18825         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
18826         
18827         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
18828         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
18829          /*
18830         
18831         var ans = this.editorcore.getAllAncestors();
18832         if (this.formatCombo) {
18833             
18834             
18835             var store = this.formatCombo.store;
18836             this.formatCombo.setValue("");
18837             for (var i =0; i < ans.length;i++) {
18838                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
18839                     // select it..
18840                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
18841                     break;
18842                 }
18843             }
18844         }
18845         
18846         
18847         
18848         // hides menus... - so this cant be on a menu...
18849         Roo.bootstrap.MenuMgr.hideAll();
18850         */
18851         Roo.bootstrap.MenuMgr.hideAll();
18852         //this.editorsyncValue();
18853     },
18854     onFirstFocus: function() {
18855         this.buttons.each(function(item){
18856            item.enable();
18857         });
18858     },
18859     toggleSourceEdit : function(sourceEditMode){
18860         
18861           
18862         if(sourceEditMode){
18863             Roo.log("disabling buttons");
18864            this.buttons.each( function(item){
18865                 if(item.cmd != 'pencil'){
18866                     item.disable();
18867                 }
18868             });
18869           
18870         }else{
18871             Roo.log("enabling buttons");
18872             if(this.editorcore.initialized){
18873                 this.buttons.each( function(item){
18874                     item.enable();
18875                 });
18876             }
18877             
18878         }
18879         Roo.log("calling toggole on editor");
18880         // tell the editor that it's been pressed..
18881         this.editor.toggleSourceEdit(sourceEditMode);
18882        
18883     }
18884 });
18885
18886
18887
18888
18889
18890 /**
18891  * @class Roo.bootstrap.Table.AbstractSelectionModel
18892  * @extends Roo.util.Observable
18893  * Abstract base class for grid SelectionModels.  It provides the interface that should be
18894  * implemented by descendant classes.  This class should not be directly instantiated.
18895  * @constructor
18896  */
18897 Roo.bootstrap.Table.AbstractSelectionModel = function(){
18898     this.locked = false;
18899     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
18900 };
18901
18902
18903 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
18904     /** @ignore Called by the grid automatically. Do not call directly. */
18905     init : function(grid){
18906         this.grid = grid;
18907         this.initEvents();
18908     },
18909
18910     /**
18911      * Locks the selections.
18912      */
18913     lock : function(){
18914         this.locked = true;
18915     },
18916
18917     /**
18918      * Unlocks the selections.
18919      */
18920     unlock : function(){
18921         this.locked = false;
18922     },
18923
18924     /**
18925      * Returns true if the selections are locked.
18926      * @return {Boolean}
18927      */
18928     isLocked : function(){
18929         return this.locked;
18930     }
18931 });
18932 /**
18933  * @extends Roo.bootstrap.Table.AbstractSelectionModel
18934  * @class Roo.bootstrap.Table.RowSelectionModel
18935  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
18936  * It supports multiple selections and keyboard selection/navigation. 
18937  * @constructor
18938  * @param {Object} config
18939  */
18940
18941 Roo.bootstrap.Table.RowSelectionModel = function(config){
18942     Roo.apply(this, config);
18943     this.selections = new Roo.util.MixedCollection(false, function(o){
18944         return o.id;
18945     });
18946
18947     this.last = false;
18948     this.lastActive = false;
18949
18950     this.addEvents({
18951         /**
18952              * @event selectionchange
18953              * Fires when the selection changes
18954              * @param {SelectionModel} this
18955              */
18956             "selectionchange" : true,
18957         /**
18958              * @event afterselectionchange
18959              * Fires after the selection changes (eg. by key press or clicking)
18960              * @param {SelectionModel} this
18961              */
18962             "afterselectionchange" : true,
18963         /**
18964              * @event beforerowselect
18965              * Fires when a row is selected being selected, return false to cancel.
18966              * @param {SelectionModel} this
18967              * @param {Number} rowIndex The selected index
18968              * @param {Boolean} keepExisting False if other selections will be cleared
18969              */
18970             "beforerowselect" : true,
18971         /**
18972              * @event rowselect
18973              * Fires when a row is selected.
18974              * @param {SelectionModel} this
18975              * @param {Number} rowIndex The selected index
18976              * @param {Roo.data.Record} r The record
18977              */
18978             "rowselect" : true,
18979         /**
18980              * @event rowdeselect
18981              * Fires when a row is deselected.
18982              * @param {SelectionModel} this
18983              * @param {Number} rowIndex The selected index
18984              */
18985         "rowdeselect" : true
18986     });
18987     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
18988     this.locked = false;
18989 };
18990
18991 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
18992     /**
18993      * @cfg {Boolean} singleSelect
18994      * True to allow selection of only one row at a time (defaults to false)
18995      */
18996     singleSelect : false,
18997
18998     // private
18999     initEvents : function(){
19000
19001         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
19002             this.grid.on("mousedown", this.handleMouseDown, this);
19003         }else{ // allow click to work like normal
19004             this.grid.on("rowclick", this.handleDragableRowClick, this);
19005         }
19006
19007         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
19008             "up" : function(e){
19009                 if(!e.shiftKey){
19010                     this.selectPrevious(e.shiftKey);
19011                 }else if(this.last !== false && this.lastActive !== false){
19012                     var last = this.last;
19013                     this.selectRange(this.last,  this.lastActive-1);
19014                     this.grid.getView().focusRow(this.lastActive);
19015                     if(last !== false){
19016                         this.last = last;
19017                     }
19018                 }else{
19019                     this.selectFirstRow();
19020                 }
19021                 this.fireEvent("afterselectionchange", this);
19022             },
19023             "down" : function(e){
19024                 if(!e.shiftKey){
19025                     this.selectNext(e.shiftKey);
19026                 }else if(this.last !== false && this.lastActive !== false){
19027                     var last = this.last;
19028                     this.selectRange(this.last,  this.lastActive+1);
19029                     this.grid.getView().focusRow(this.lastActive);
19030                     if(last !== false){
19031                         this.last = last;
19032                     }
19033                 }else{
19034                     this.selectFirstRow();
19035                 }
19036                 this.fireEvent("afterselectionchange", this);
19037             },
19038             scope: this
19039         });
19040
19041         var view = this.grid.view;
19042         view.on("refresh", this.onRefresh, this);
19043         view.on("rowupdated", this.onRowUpdated, this);
19044         view.on("rowremoved", this.onRemove, this);
19045     },
19046
19047     // private
19048     onRefresh : function(){
19049         var ds = this.grid.dataSource, i, v = this.grid.view;
19050         var s = this.selections;
19051         s.each(function(r){
19052             if((i = ds.indexOfId(r.id)) != -1){
19053                 v.onRowSelect(i);
19054             }else{
19055                 s.remove(r);
19056             }
19057         });
19058     },
19059
19060     // private
19061     onRemove : function(v, index, r){
19062         this.selections.remove(r);
19063     },
19064
19065     // private
19066     onRowUpdated : function(v, index, r){
19067         if(this.isSelected(r)){
19068             v.onRowSelect(index);
19069         }
19070     },
19071
19072     /**
19073      * Select records.
19074      * @param {Array} records The records to select
19075      * @param {Boolean} keepExisting (optional) True to keep existing selections
19076      */
19077     selectRecords : function(records, keepExisting){
19078         if(!keepExisting){
19079             this.clearSelections();
19080         }
19081         var ds = this.grid.dataSource;
19082         for(var i = 0, len = records.length; i < len; i++){
19083             this.selectRow(ds.indexOf(records[i]), true);
19084         }
19085     },
19086
19087     /**
19088      * Gets the number of selected rows.
19089      * @return {Number}
19090      */
19091     getCount : function(){
19092         return this.selections.length;
19093     },
19094
19095     /**
19096      * Selects the first row in the grid.
19097      */
19098     selectFirstRow : function(){
19099         this.selectRow(0);
19100     },
19101
19102     /**
19103      * Select the last row.
19104      * @param {Boolean} keepExisting (optional) True to keep existing selections
19105      */
19106     selectLastRow : function(keepExisting){
19107         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
19108     },
19109
19110     /**
19111      * Selects the row immediately following the last selected row.
19112      * @param {Boolean} keepExisting (optional) True to keep existing selections
19113      */
19114     selectNext : function(keepExisting){
19115         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
19116             this.selectRow(this.last+1, keepExisting);
19117             this.grid.getView().focusRow(this.last);
19118         }
19119     },
19120
19121     /**
19122      * Selects the row that precedes the last selected row.
19123      * @param {Boolean} keepExisting (optional) True to keep existing selections
19124      */
19125     selectPrevious : function(keepExisting){
19126         if(this.last){
19127             this.selectRow(this.last-1, keepExisting);
19128             this.grid.getView().focusRow(this.last);
19129         }
19130     },
19131
19132     /**
19133      * Returns the selected records
19134      * @return {Array} Array of selected records
19135      */
19136     getSelections : function(){
19137         return [].concat(this.selections.items);
19138     },
19139
19140     /**
19141      * Returns the first selected record.
19142      * @return {Record}
19143      */
19144     getSelected : function(){
19145         return this.selections.itemAt(0);
19146     },
19147
19148
19149     /**
19150      * Clears all selections.
19151      */
19152     clearSelections : function(fast){
19153         if(this.locked) return;
19154         if(fast !== true){
19155             var ds = this.grid.dataSource;
19156             var s = this.selections;
19157             s.each(function(r){
19158                 this.deselectRow(ds.indexOfId(r.id));
19159             }, this);
19160             s.clear();
19161         }else{
19162             this.selections.clear();
19163         }
19164         this.last = false;
19165     },
19166
19167
19168     /**
19169      * Selects all rows.
19170      */
19171     selectAll : function(){
19172         if(this.locked) return;
19173         this.selections.clear();
19174         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
19175             this.selectRow(i, true);
19176         }
19177     },
19178
19179     /**
19180      * Returns True if there is a selection.
19181      * @return {Boolean}
19182      */
19183     hasSelection : function(){
19184         return this.selections.length > 0;
19185     },
19186
19187     /**
19188      * Returns True if the specified row is selected.
19189      * @param {Number/Record} record The record or index of the record to check
19190      * @return {Boolean}
19191      */
19192     isSelected : function(index){
19193         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
19194         return (r && this.selections.key(r.id) ? true : false);
19195     },
19196
19197     /**
19198      * Returns True if the specified record id is selected.
19199      * @param {String} id The id of record to check
19200      * @return {Boolean}
19201      */
19202     isIdSelected : function(id){
19203         return (this.selections.key(id) ? true : false);
19204     },
19205
19206     // private
19207     handleMouseDown : function(e, t){
19208         var view = this.grid.getView(), rowIndex;
19209         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
19210             return;
19211         };
19212         if(e.shiftKey && this.last !== false){
19213             var last = this.last;
19214             this.selectRange(last, rowIndex, e.ctrlKey);
19215             this.last = last; // reset the last
19216             view.focusRow(rowIndex);
19217         }else{
19218             var isSelected = this.isSelected(rowIndex);
19219             if(e.button !== 0 && isSelected){
19220                 view.focusRow(rowIndex);
19221             }else if(e.ctrlKey && isSelected){
19222                 this.deselectRow(rowIndex);
19223             }else if(!isSelected){
19224                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
19225                 view.focusRow(rowIndex);
19226             }
19227         }
19228         this.fireEvent("afterselectionchange", this);
19229     },
19230     // private
19231     handleDragableRowClick :  function(grid, rowIndex, e) 
19232     {
19233         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
19234             this.selectRow(rowIndex, false);
19235             grid.view.focusRow(rowIndex);
19236              this.fireEvent("afterselectionchange", this);
19237         }
19238     },
19239     
19240     /**
19241      * Selects multiple rows.
19242      * @param {Array} rows Array of the indexes of the row to select
19243      * @param {Boolean} keepExisting (optional) True to keep existing selections
19244      */
19245     selectRows : function(rows, keepExisting){
19246         if(!keepExisting){
19247             this.clearSelections();
19248         }
19249         for(var i = 0, len = rows.length; i < len; i++){
19250             this.selectRow(rows[i], true);
19251         }
19252     },
19253
19254     /**
19255      * Selects a range of rows. All rows in between startRow and endRow are also selected.
19256      * @param {Number} startRow The index of the first row in the range
19257      * @param {Number} endRow The index of the last row in the range
19258      * @param {Boolean} keepExisting (optional) True to retain existing selections
19259      */
19260     selectRange : function(startRow, endRow, keepExisting){
19261         if(this.locked) return;
19262         if(!keepExisting){
19263             this.clearSelections();
19264         }
19265         if(startRow <= endRow){
19266             for(var i = startRow; i <= endRow; i++){
19267                 this.selectRow(i, true);
19268             }
19269         }else{
19270             for(var i = startRow; i >= endRow; i--){
19271                 this.selectRow(i, true);
19272             }
19273         }
19274     },
19275
19276     /**
19277      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
19278      * @param {Number} startRow The index of the first row in the range
19279      * @param {Number} endRow The index of the last row in the range
19280      */
19281     deselectRange : function(startRow, endRow, preventViewNotify){
19282         if(this.locked) return;
19283         for(var i = startRow; i <= endRow; i++){
19284             this.deselectRow(i, preventViewNotify);
19285         }
19286     },
19287
19288     /**
19289      * Selects a row.
19290      * @param {Number} row The index of the row to select
19291      * @param {Boolean} keepExisting (optional) True to keep existing selections
19292      */
19293     selectRow : function(index, keepExisting, preventViewNotify){
19294         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
19295         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
19296             if(!keepExisting || this.singleSelect){
19297                 this.clearSelections();
19298             }
19299             var r = this.grid.dataSource.getAt(index);
19300             this.selections.add(r);
19301             this.last = this.lastActive = index;
19302             if(!preventViewNotify){
19303                 this.grid.getView().onRowSelect(index);
19304             }
19305             this.fireEvent("rowselect", this, index, r);
19306             this.fireEvent("selectionchange", this);
19307         }
19308     },
19309
19310     /**
19311      * Deselects a row.
19312      * @param {Number} row The index of the row to deselect
19313      */
19314     deselectRow : function(index, preventViewNotify){
19315         if(this.locked) return;
19316         if(this.last == index){
19317             this.last = false;
19318         }
19319         if(this.lastActive == index){
19320             this.lastActive = false;
19321         }
19322         var r = this.grid.dataSource.getAt(index);
19323         this.selections.remove(r);
19324         if(!preventViewNotify){
19325             this.grid.getView().onRowDeselect(index);
19326         }
19327         this.fireEvent("rowdeselect", this, index);
19328         this.fireEvent("selectionchange", this);
19329     },
19330
19331     // private
19332     restoreLast : function(){
19333         if(this._last){
19334             this.last = this._last;
19335         }
19336     },
19337
19338     // private
19339     acceptsNav : function(row, col, cm){
19340         return !cm.isHidden(col) && cm.isCellEditable(col, row);
19341     },
19342
19343     // private
19344     onEditorKey : function(field, e){
19345         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
19346         if(k == e.TAB){
19347             e.stopEvent();
19348             ed.completeEdit();
19349             if(e.shiftKey){
19350                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
19351             }else{
19352                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
19353             }
19354         }else if(k == e.ENTER && !e.ctrlKey){
19355             e.stopEvent();
19356             ed.completeEdit();
19357             if(e.shiftKey){
19358                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
19359             }else{
19360                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
19361             }
19362         }else if(k == e.ESC){
19363             ed.cancelEdit();
19364         }
19365         if(newCell){
19366             g.startEditing(newCell[0], newCell[1]);
19367         }
19368     }
19369 });/*
19370  * Based on:
19371  * Ext JS Library 1.1.1
19372  * Copyright(c) 2006-2007, Ext JS, LLC.
19373  *
19374  * Originally Released Under LGPL - original licence link has changed is not relivant.
19375  *
19376  * Fork - LGPL
19377  * <script type="text/javascript">
19378  */
19379  
19380 /**
19381  * @class Roo.bootstrap.PagingToolbar
19382  * @extends Roo.Row
19383  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
19384  * @constructor
19385  * Create a new PagingToolbar
19386  * @param {Object} config The config object
19387  */
19388 Roo.bootstrap.PagingToolbar = function(config)
19389 {
19390     // old args format still supported... - xtype is prefered..
19391         // created from xtype...
19392     var ds = config.dataSource;
19393     this.toolbarItems = [];
19394     if (config.items) {
19395         this.toolbarItems = config.items;
19396 //        config.items = [];
19397     }
19398     
19399     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
19400     this.ds = ds;
19401     this.cursor = 0;
19402     if (ds) { 
19403         this.bind(ds);
19404     }
19405     
19406     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
19407     
19408 };
19409
19410 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
19411     /**
19412      * @cfg {Roo.data.Store} dataSource
19413      * The underlying data store providing the paged data
19414      */
19415     /**
19416      * @cfg {String/HTMLElement/Element} container
19417      * container The id or element that will contain the toolbar
19418      */
19419     /**
19420      * @cfg {Boolean} displayInfo
19421      * True to display the displayMsg (defaults to false)
19422      */
19423     /**
19424      * @cfg {Number} pageSize
19425      * The number of records to display per page (defaults to 20)
19426      */
19427     pageSize: 20,
19428     /**
19429      * @cfg {String} displayMsg
19430      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
19431      */
19432     displayMsg : 'Displaying {0} - {1} of {2}',
19433     /**
19434      * @cfg {String} emptyMsg
19435      * The message to display when no records are found (defaults to "No data to display")
19436      */
19437     emptyMsg : 'No data to display',
19438     /**
19439      * Customizable piece of the default paging text (defaults to "Page")
19440      * @type String
19441      */
19442     beforePageText : "Page",
19443     /**
19444      * Customizable piece of the default paging text (defaults to "of %0")
19445      * @type String
19446      */
19447     afterPageText : "of {0}",
19448     /**
19449      * Customizable piece of the default paging text (defaults to "First Page")
19450      * @type String
19451      */
19452     firstText : "First Page",
19453     /**
19454      * Customizable piece of the default paging text (defaults to "Previous Page")
19455      * @type String
19456      */
19457     prevText : "Previous Page",
19458     /**
19459      * Customizable piece of the default paging text (defaults to "Next Page")
19460      * @type String
19461      */
19462     nextText : "Next Page",
19463     /**
19464      * Customizable piece of the default paging text (defaults to "Last Page")
19465      * @type String
19466      */
19467     lastText : "Last Page",
19468     /**
19469      * Customizable piece of the default paging text (defaults to "Refresh")
19470      * @type String
19471      */
19472     refreshText : "Refresh",
19473
19474     buttons : false,
19475     // private
19476     onRender : function(ct, position) 
19477     {
19478         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
19479         this.navgroup.parentId = this.id;
19480         this.navgroup.onRender(this.el, null);
19481         // add the buttons to the navgroup
19482         
19483         if(this.displayInfo){
19484             Roo.log(this.el.select('ul.navbar-nav',true).first());
19485             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
19486             this.displayEl = this.el.select('.x-paging-info', true).first();
19487 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
19488 //            this.displayEl = navel.el.select('span',true).first();
19489         }
19490         
19491         var _this = this;
19492         
19493         if(this.buttons){
19494             Roo.each(_this.buttons, function(e){
19495                Roo.factory(e).onRender(_this.el, null);
19496             });
19497         }
19498             
19499         Roo.each(_this.toolbarItems, function(e) {
19500             _this.navgroup.addItem(e);
19501         });
19502         
19503         this.first = this.navgroup.addItem({
19504             tooltip: this.firstText,
19505             cls: "prev",
19506             icon : 'fa fa-backward',
19507             disabled: true,
19508             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
19509         });
19510         
19511         this.prev =  this.navgroup.addItem({
19512             tooltip: this.prevText,
19513             cls: "prev",
19514             icon : 'fa fa-step-backward',
19515             disabled: true,
19516             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
19517         });
19518     //this.addSeparator();
19519         
19520         
19521         var field = this.navgroup.addItem( {
19522             tagtype : 'span',
19523             cls : 'x-paging-position',
19524             
19525             html : this.beforePageText  +
19526                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
19527                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
19528          } ); //?? escaped?
19529         
19530         this.field = field.el.select('input', true).first();
19531         this.field.on("keydown", this.onPagingKeydown, this);
19532         this.field.on("focus", function(){this.dom.select();});
19533     
19534     
19535         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
19536         //this.field.setHeight(18);
19537         //this.addSeparator();
19538         this.next = this.navgroup.addItem({
19539             tooltip: this.nextText,
19540             cls: "next",
19541             html : ' <i class="fa fa-step-forward">',
19542             disabled: true,
19543             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
19544         });
19545         this.last = this.navgroup.addItem({
19546             tooltip: this.lastText,
19547             icon : 'fa fa-forward',
19548             cls: "next",
19549             disabled: true,
19550             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
19551         });
19552     //this.addSeparator();
19553         this.loading = this.navgroup.addItem({
19554             tooltip: this.refreshText,
19555             icon: 'fa fa-refresh',
19556             
19557             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
19558         });
19559
19560     },
19561
19562     // private
19563     updateInfo : function(){
19564         if(this.displayEl){
19565             var count = this.ds.getCount();
19566             var msg = count == 0 ?
19567                 this.emptyMsg :
19568                 String.format(
19569                     this.displayMsg,
19570                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
19571                 );
19572             this.displayEl.update(msg);
19573         }
19574     },
19575
19576     // private
19577     onLoad : function(ds, r, o){
19578        this.cursor = o.params ? o.params.start : 0;
19579        var d = this.getPageData(),
19580             ap = d.activePage,
19581             ps = d.pages;
19582         
19583        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
19584        this.field.dom.value = ap;
19585        this.first.setDisabled(ap == 1);
19586        this.prev.setDisabled(ap == 1);
19587        this.next.setDisabled(ap == ps);
19588        this.last.setDisabled(ap == ps);
19589        this.loading.enable();
19590        this.updateInfo();
19591     },
19592
19593     // private
19594     getPageData : function(){
19595         var total = this.ds.getTotalCount();
19596         return {
19597             total : total,
19598             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
19599             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
19600         };
19601     },
19602
19603     // private
19604     onLoadError : function(){
19605         this.loading.enable();
19606     },
19607
19608     // private
19609     onPagingKeydown : function(e){
19610         var k = e.getKey();
19611         var d = this.getPageData();
19612         if(k == e.RETURN){
19613             var v = this.field.dom.value, pageNum;
19614             if(!v || isNaN(pageNum = parseInt(v, 10))){
19615                 this.field.dom.value = d.activePage;
19616                 return;
19617             }
19618             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
19619             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
19620             e.stopEvent();
19621         }
19622         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))
19623         {
19624           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
19625           this.field.dom.value = pageNum;
19626           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
19627           e.stopEvent();
19628         }
19629         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
19630         {
19631           var v = this.field.dom.value, pageNum; 
19632           var increment = (e.shiftKey) ? 10 : 1;
19633           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
19634             increment *= -1;
19635           if(!v || isNaN(pageNum = parseInt(v, 10))) {
19636             this.field.dom.value = d.activePage;
19637             return;
19638           }
19639           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
19640           {
19641             this.field.dom.value = parseInt(v, 10) + increment;
19642             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
19643             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
19644           }
19645           e.stopEvent();
19646         }
19647     },
19648
19649     // private
19650     beforeLoad : function(){
19651         if(this.loading){
19652             this.loading.disable();
19653         }
19654     },
19655
19656     // private
19657     onClick : function(which){
19658         var ds = this.ds;
19659         if (!ds) {
19660             return;
19661         }
19662         switch(which){
19663             case "first":
19664                 ds.load({params:{start: 0, limit: this.pageSize}});
19665             break;
19666             case "prev":
19667                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
19668             break;
19669             case "next":
19670                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
19671             break;
19672             case "last":
19673                 var total = ds.getTotalCount();
19674                 var extra = total % this.pageSize;
19675                 var lastStart = extra ? (total - extra) : total-this.pageSize;
19676                 ds.load({params:{start: lastStart, limit: this.pageSize}});
19677             break;
19678             case "refresh":
19679                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
19680             break;
19681         }
19682     },
19683
19684     /**
19685      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
19686      * @param {Roo.data.Store} store The data store to unbind
19687      */
19688     unbind : function(ds){
19689         ds.un("beforeload", this.beforeLoad, this);
19690         ds.un("load", this.onLoad, this);
19691         ds.un("loadexception", this.onLoadError, this);
19692         ds.un("remove", this.updateInfo, this);
19693         ds.un("add", this.updateInfo, this);
19694         this.ds = undefined;
19695     },
19696
19697     /**
19698      * Binds the paging toolbar to the specified {@link Roo.data.Store}
19699      * @param {Roo.data.Store} store The data store to bind
19700      */
19701     bind : function(ds){
19702         ds.on("beforeload", this.beforeLoad, this);
19703         ds.on("load", this.onLoad, this);
19704         ds.on("loadexception", this.onLoadError, this);
19705         ds.on("remove", this.updateInfo, this);
19706         ds.on("add", this.updateInfo, this);
19707         this.ds = ds;
19708     }
19709 });/*
19710  * - LGPL
19711  *
19712  * element
19713  * 
19714  */
19715
19716 /**
19717  * @class Roo.bootstrap.MessageBar
19718  * @extends Roo.bootstrap.Component
19719  * Bootstrap MessageBar class
19720  * @cfg {String} html contents of the MessageBar
19721  * @cfg {String} weight (info | success | warning | danger) default info
19722  * @cfg {String} beforeClass insert the bar before the given class
19723  * @cfg {Boolean} closable (true | false) default false
19724  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
19725  * 
19726  * @constructor
19727  * Create a new Element
19728  * @param {Object} config The config object
19729  */
19730
19731 Roo.bootstrap.MessageBar = function(config){
19732     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
19733 };
19734
19735 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
19736     
19737     html: '',
19738     weight: 'info',
19739     closable: false,
19740     fixed: false,
19741     beforeClass: 'bootstrap-sticky-wrap',
19742     
19743     getAutoCreate : function(){
19744         
19745         var cfg = {
19746             tag: 'div',
19747             cls: 'alert alert-dismissable alert-' + this.weight,
19748             cn: [
19749                 {
19750                     tag: 'span',
19751                     cls: 'message',
19752                     html: this.html || ''
19753                 }
19754             ]
19755         }
19756         
19757         if(this.fixed){
19758             cfg.cls += ' alert-messages-fixed';
19759         }
19760         
19761         if(this.closable){
19762             cfg.cn.push({
19763                 tag: 'button',
19764                 cls: 'close',
19765                 html: 'x'
19766             });
19767         }
19768         
19769         return cfg;
19770     },
19771     
19772     onRender : function(ct, position)
19773     {
19774         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19775         
19776         if(!this.el){
19777             var cfg = Roo.apply({},  this.getAutoCreate());
19778             cfg.id = Roo.id();
19779             
19780             if (this.cls) {
19781                 cfg.cls += ' ' + this.cls;
19782             }
19783             if (this.style) {
19784                 cfg.style = this.style;
19785             }
19786             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
19787             
19788             this.el.setVisibilityMode(Roo.Element.DISPLAY);
19789         }
19790         
19791         this.el.select('>button.close').on('click', this.hide, this);
19792         
19793     },
19794     
19795     show : function()
19796     {
19797         if (!this.rendered) {
19798             this.render();
19799         }
19800         
19801         this.el.show();
19802         
19803         this.fireEvent('show', this);
19804         
19805     },
19806     
19807     hide : function()
19808     {
19809         if (!this.rendered) {
19810             this.render();
19811         }
19812         
19813         this.el.hide();
19814         
19815         this.fireEvent('hide', this);
19816     },
19817     
19818     update : function()
19819     {
19820 //        var e = this.el.dom.firstChild;
19821 //        
19822 //        if(this.closable){
19823 //            e = e.nextSibling;
19824 //        }
19825 //        
19826 //        e.data = this.html || '';
19827
19828         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
19829     }
19830    
19831 });
19832
19833  
19834
19835      /*
19836  * - LGPL
19837  *
19838  * Graph
19839  * 
19840  */
19841
19842
19843 /**
19844  * @class Roo.bootstrap.Graph
19845  * @extends Roo.bootstrap.Component
19846  * Bootstrap Graph class
19847 > Prameters
19848  -sm {number} sm 4
19849  -md {number} md 5
19850  @cfg {String} graphtype  bar | vbar | pie
19851  @cfg {number} g_x coodinator | centre x (pie)
19852  @cfg {number} g_y coodinator | centre y (pie)
19853  @cfg {number} g_r radius (pie)
19854  @cfg {number} g_height height of the chart (respected by all elements in the set)
19855  @cfg {number} g_width width of the chart (respected by all elements in the set)
19856  @cfg {Object} title The title of the chart
19857     
19858  -{Array}  values
19859  -opts (object) options for the chart 
19860      o {
19861      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
19862      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
19863      o vgutter (number)
19864      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.
19865      o stacked (boolean) whether or not to tread values as in a stacked bar chart
19866      o to
19867      o stretch (boolean)
19868      o }
19869  -opts (object) options for the pie
19870      o{
19871      o cut
19872      o startAngle (number)
19873      o endAngle (number)
19874      } 
19875  *
19876  * @constructor
19877  * Create a new Input
19878  * @param {Object} config The config object
19879  */
19880
19881 Roo.bootstrap.Graph = function(config){
19882     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
19883     
19884     this.addEvents({
19885         // img events
19886         /**
19887          * @event click
19888          * The img click event for the img.
19889          * @param {Roo.EventObject} e
19890          */
19891         "click" : true
19892     });
19893 };
19894
19895 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
19896     
19897     sm: 4,
19898     md: 5,
19899     graphtype: 'bar',
19900     g_height: 250,
19901     g_width: 400,
19902     g_x: 50,
19903     g_y: 50,
19904     g_r: 30,
19905     opts:{
19906         //g_colors: this.colors,
19907         g_type: 'soft',
19908         g_gutter: '20%'
19909
19910     },
19911     title : false,
19912
19913     getAutoCreate : function(){
19914         
19915         var cfg = {
19916             tag: 'div',
19917             html : null
19918         }
19919         
19920         
19921         return  cfg;
19922     },
19923
19924     onRender : function(ct,position){
19925         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
19926         this.raphael = Raphael(this.el.dom);
19927         
19928                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19929                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19930                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19931                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
19932                 /*
19933                 r.text(160, 10, "Single Series Chart").attr(txtattr);
19934                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
19935                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
19936                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
19937                 
19938                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
19939                 r.barchart(330, 10, 300, 220, data1);
19940                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
19941                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
19942                 */
19943                 
19944                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19945                 // r.barchart(30, 30, 560, 250,  xdata, {
19946                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
19947                 //     axis : "0 0 1 1",
19948                 //     axisxlabels :  xdata
19949                 //     //yvalues : cols,
19950                    
19951                 // });
19952 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19953 //        
19954 //        this.load(null,xdata,{
19955 //                axis : "0 0 1 1",
19956 //                axisxlabels :  xdata
19957 //                });
19958
19959     },
19960
19961     load : function(graphtype,xdata,opts){
19962         this.raphael.clear();
19963         if(!graphtype) {
19964             graphtype = this.graphtype;
19965         }
19966         if(!opts){
19967             opts = this.opts;
19968         }
19969         var r = this.raphael,
19970             fin = function () {
19971                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
19972             },
19973             fout = function () {
19974                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
19975             },
19976             pfin = function() {
19977                 this.sector.stop();
19978                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
19979
19980                 if (this.label) {
19981                     this.label[0].stop();
19982                     this.label[0].attr({ r: 7.5 });
19983                     this.label[1].attr({ "font-weight": 800 });
19984                 }
19985             },
19986             pfout = function() {
19987                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
19988
19989                 if (this.label) {
19990                     this.label[0].animate({ r: 5 }, 500, "bounce");
19991                     this.label[1].attr({ "font-weight": 400 });
19992                 }
19993             };
19994
19995         switch(graphtype){
19996             case 'bar':
19997                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19998                 break;
19999             case 'hbar':
20000                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
20001                 break;
20002             case 'pie':
20003 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
20004 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
20005 //            
20006                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
20007                 
20008                 break;
20009
20010         }
20011         
20012         if(this.title){
20013             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
20014         }
20015         
20016     },
20017     
20018     setTitle: function(o)
20019     {
20020         this.title = o;
20021     },
20022     
20023     initEvents: function() {
20024         
20025         if(!this.href){
20026             this.el.on('click', this.onClick, this);
20027         }
20028     },
20029     
20030     onClick : function(e)
20031     {
20032         Roo.log('img onclick');
20033         this.fireEvent('click', this, e);
20034     }
20035    
20036 });
20037
20038  
20039 /*
20040  * - LGPL
20041  *
20042  * numberBox
20043  * 
20044  */
20045 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
20046
20047 /**
20048  * @class Roo.bootstrap.dash.NumberBox
20049  * @extends Roo.bootstrap.Component
20050  * Bootstrap NumberBox class
20051  * @cfg {String} headline Box headline
20052  * @cfg {String} content Box content
20053  * @cfg {String} icon Box icon
20054  * @cfg {String} footer Footer text
20055  * @cfg {String} fhref Footer href
20056  * 
20057  * @constructor
20058  * Create a new NumberBox
20059  * @param {Object} config The config object
20060  */
20061
20062
20063 Roo.bootstrap.dash.NumberBox = function(config){
20064     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
20065     
20066 };
20067
20068 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
20069     
20070     headline : '',
20071     content : '',
20072     icon : '',
20073     footer : '',
20074     fhref : '',
20075     ficon : '',
20076     
20077     getAutoCreate : function(){
20078         
20079         var cfg = {
20080             tag : 'div',
20081             cls : 'small-box ',
20082             cn : [
20083                 {
20084                     tag : 'div',
20085                     cls : 'inner',
20086                     cn :[
20087                         {
20088                             tag : 'h3',
20089                             cls : 'roo-headline',
20090                             html : this.headline
20091                         },
20092                         {
20093                             tag : 'p',
20094                             cls : 'roo-content',
20095                             html : this.content
20096                         }
20097                     ]
20098                 }
20099             ]
20100         }
20101         
20102         if(this.icon){
20103             cfg.cn.push({
20104                 tag : 'div',
20105                 cls : 'icon',
20106                 cn :[
20107                     {
20108                         tag : 'i',
20109                         cls : 'ion ' + this.icon
20110                     }
20111                 ]
20112             });
20113         }
20114         
20115         if(this.footer){
20116             var footer = {
20117                 tag : 'a',
20118                 cls : 'small-box-footer',
20119                 href : this.fhref || '#',
20120                 html : this.footer
20121             };
20122             
20123             cfg.cn.push(footer);
20124             
20125         }
20126         
20127         return  cfg;
20128     },
20129
20130     onRender : function(ct,position){
20131         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
20132
20133
20134        
20135                 
20136     },
20137
20138     setHeadline: function (value)
20139     {
20140         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
20141     },
20142     
20143     setFooter: function (value, href)
20144     {
20145         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
20146         
20147         if(href){
20148             this.el.select('a.small-box-footer',true).first().attr('href', href);
20149         }
20150         
20151     },
20152
20153     setContent: function (value)
20154     {
20155         this.el.select('.roo-content',true).first().dom.innerHTML = value;
20156     },
20157
20158     initEvents: function() 
20159     {   
20160         
20161     }
20162     
20163 });
20164
20165  
20166 /*
20167  * - LGPL
20168  *
20169  * TabBox
20170  * 
20171  */
20172 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
20173
20174 /**
20175  * @class Roo.bootstrap.dash.TabBox
20176  * @extends Roo.bootstrap.Component
20177  * Bootstrap TabBox class
20178  * @cfg {String} title Title of the TabBox
20179  * @cfg {String} icon Icon of the TabBox
20180  * @cfg {Boolean} showtabs (true|false) show the tabs default true
20181  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
20182  * 
20183  * @constructor
20184  * Create a new TabBox
20185  * @param {Object} config The config object
20186  */
20187
20188
20189 Roo.bootstrap.dash.TabBox = function(config){
20190     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
20191     this.addEvents({
20192         // raw events
20193         /**
20194          * @event addpane
20195          * When a pane is added
20196          * @param {Roo.bootstrap.dash.TabPane} pane
20197          */
20198         "addpane" : true,
20199         /**
20200          * @event activatepane
20201          * When a pane is activated
20202          * @param {Roo.bootstrap.dash.TabPane} pane
20203          */
20204         "activatepane" : true
20205         
20206          
20207     });
20208     
20209     this.panes = [];
20210 };
20211
20212 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
20213
20214     title : '',
20215     icon : false,
20216     showtabs : true,
20217     tabScrollable : false,
20218     
20219     getChildContainer : function()
20220     {
20221         return this.el.select('.tab-content', true).first();
20222     },
20223     
20224     getAutoCreate : function(){
20225         
20226         var header = {
20227             tag: 'li',
20228             cls: 'pull-left header',
20229             html: this.title,
20230             cn : []
20231         };
20232         
20233         if(this.icon){
20234             header.cn.push({
20235                 tag: 'i',
20236                 cls: 'fa ' + this.icon
20237             });
20238         }
20239         
20240         var h = {
20241             tag: 'ul',
20242             cls: 'nav nav-tabs pull-right',
20243             cn: [
20244                 header
20245             ]
20246         };
20247         
20248         if(this.tabScrollable){
20249             h = {
20250                 tag: 'div',
20251                 cls: 'tab-header',
20252                 cn: [
20253                     {
20254                         tag: 'ul',
20255                         cls: 'nav nav-tabs pull-right',
20256                         cn: [
20257                             header
20258                         ]
20259                     }
20260                 ]
20261             }
20262         }
20263         
20264         var cfg = {
20265             tag: 'div',
20266             cls: 'nav-tabs-custom',
20267             cn: [
20268                 h,
20269                 {
20270                     tag: 'div',
20271                     cls: 'tab-content no-padding',
20272                     cn: []
20273                 }
20274             ]
20275         }
20276
20277         return  cfg;
20278     },
20279     initEvents : function()
20280     {
20281         //Roo.log('add add pane handler');
20282         this.on('addpane', this.onAddPane, this);
20283     },
20284      /**
20285      * Updates the box title
20286      * @param {String} html to set the title to.
20287      */
20288     setTitle : function(value)
20289     {
20290         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
20291     },
20292     onAddPane : function(pane)
20293     {
20294         this.panes.push(pane);
20295         //Roo.log('addpane');
20296         //Roo.log(pane);
20297         // tabs are rendere left to right..
20298         if(!this.showtabs){
20299             return;
20300         }
20301         
20302         var ctr = this.el.select('.nav-tabs', true).first();
20303          
20304          
20305         var existing = ctr.select('.nav-tab',true);
20306         var qty = existing.getCount();;
20307         
20308         
20309         var tab = ctr.createChild({
20310             tag : 'li',
20311             cls : 'nav-tab' + (qty ? '' : ' active'),
20312             cn : [
20313                 {
20314                     tag : 'a',
20315                     href:'#',
20316                     html : pane.title
20317                 }
20318             ]
20319         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
20320         pane.tab = tab;
20321         
20322         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
20323         if (!qty) {
20324             pane.el.addClass('active');
20325         }
20326         
20327                 
20328     },
20329     onTabClick : function(ev,un,ob,pane)
20330     {
20331         //Roo.log('tab - prev default');
20332         ev.preventDefault();
20333         
20334         
20335         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
20336         pane.tab.addClass('active');
20337         //Roo.log(pane.title);
20338         this.getChildContainer().select('.tab-pane',true).removeClass('active');
20339         // technically we should have a deactivate event.. but maybe add later.
20340         // and it should not de-activate the selected tab...
20341         this.fireEvent('activatepane', pane);
20342         pane.el.addClass('active');
20343         pane.fireEvent('activate');
20344         
20345         
20346     },
20347     
20348     getActivePane : function()
20349     {
20350         var r = false;
20351         Roo.each(this.panes, function(p) {
20352             if(p.el.hasClass('active')){
20353                 r = p;
20354                 return false;
20355             }
20356             
20357             return;
20358         });
20359         
20360         return r;
20361     }
20362     
20363     
20364 });
20365
20366  
20367 /*
20368  * - LGPL
20369  *
20370  * Tab pane
20371  * 
20372  */
20373 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
20374 /**
20375  * @class Roo.bootstrap.TabPane
20376  * @extends Roo.bootstrap.Component
20377  * Bootstrap TabPane class
20378  * @cfg {Boolean} active (false | true) Default false
20379  * @cfg {String} title title of panel
20380
20381  * 
20382  * @constructor
20383  * Create a new TabPane
20384  * @param {Object} config The config object
20385  */
20386
20387 Roo.bootstrap.dash.TabPane = function(config){
20388     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
20389     
20390     this.addEvents({
20391         // raw events
20392         /**
20393          * @event activate
20394          * When a pane is activated
20395          * @param {Roo.bootstrap.dash.TabPane} pane
20396          */
20397         "activate" : true
20398          
20399     });
20400 };
20401
20402 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
20403     
20404     active : false,
20405     title : '',
20406     
20407     // the tabBox that this is attached to.
20408     tab : false,
20409      
20410     getAutoCreate : function() 
20411     {
20412         var cfg = {
20413             tag: 'div',
20414             cls: 'tab-pane'
20415         }
20416         
20417         if(this.active){
20418             cfg.cls += ' active';
20419         }
20420         
20421         return cfg;
20422     },
20423     initEvents  : function()
20424     {
20425         //Roo.log('trigger add pane handler');
20426         this.parent().fireEvent('addpane', this)
20427     },
20428     
20429      /**
20430      * Updates the tab title 
20431      * @param {String} html to set the title to.
20432      */
20433     setTitle: function(str)
20434     {
20435         if (!this.tab) {
20436             return;
20437         }
20438         this.title = str;
20439         this.tab.select('a', true).first().dom.innerHTML = str;
20440         
20441     }
20442     
20443     
20444     
20445 });
20446
20447  
20448
20449
20450  /*
20451  * - LGPL
20452  *
20453  * menu
20454  * 
20455  */
20456 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20457
20458 /**
20459  * @class Roo.bootstrap.menu.Menu
20460  * @extends Roo.bootstrap.Component
20461  * Bootstrap Menu class - container for Menu
20462  * @cfg {String} html Text of the menu
20463  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
20464  * @cfg {String} icon Font awesome icon
20465  * @cfg {String} pos Menu align to (top | bottom) default bottom
20466  * 
20467  * 
20468  * @constructor
20469  * Create a new Menu
20470  * @param {Object} config The config object
20471  */
20472
20473
20474 Roo.bootstrap.menu.Menu = function(config){
20475     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
20476     
20477     this.addEvents({
20478         /**
20479          * @event beforeshow
20480          * Fires before this menu is displayed
20481          * @param {Roo.bootstrap.menu.Menu} this
20482          */
20483         beforeshow : true,
20484         /**
20485          * @event beforehide
20486          * Fires before this menu is hidden
20487          * @param {Roo.bootstrap.menu.Menu} this
20488          */
20489         beforehide : true,
20490         /**
20491          * @event show
20492          * Fires after this menu is displayed
20493          * @param {Roo.bootstrap.menu.Menu} this
20494          */
20495         show : true,
20496         /**
20497          * @event hide
20498          * Fires after this menu is hidden
20499          * @param {Roo.bootstrap.menu.Menu} this
20500          */
20501         hide : true,
20502         /**
20503          * @event click
20504          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
20505          * @param {Roo.bootstrap.menu.Menu} this
20506          * @param {Roo.EventObject} e
20507          */
20508         click : true
20509     });
20510     
20511 };
20512
20513 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
20514     
20515     submenu : false,
20516     html : '',
20517     weight : 'default',
20518     icon : false,
20519     pos : 'bottom',
20520     
20521     
20522     getChildContainer : function() {
20523         if(this.isSubMenu){
20524             return this.el;
20525         }
20526         
20527         return this.el.select('ul.dropdown-menu', true).first();  
20528     },
20529     
20530     getAutoCreate : function()
20531     {
20532         var text = [
20533             {
20534                 tag : 'span',
20535                 cls : 'roo-menu-text',
20536                 html : this.html
20537             }
20538         ];
20539         
20540         if(this.icon){
20541             text.unshift({
20542                 tag : 'i',
20543                 cls : 'fa ' + this.icon
20544             })
20545         }
20546         
20547         
20548         var cfg = {
20549             tag : 'div',
20550             cls : 'btn-group',
20551             cn : [
20552                 {
20553                     tag : 'button',
20554                     cls : 'dropdown-button btn btn-' + this.weight,
20555                     cn : text
20556                 },
20557                 {
20558                     tag : 'button',
20559                     cls : 'dropdown-toggle btn btn-' + this.weight,
20560                     cn : [
20561                         {
20562                             tag : 'span',
20563                             cls : 'caret'
20564                         }
20565                     ]
20566                 },
20567                 {
20568                     tag : 'ul',
20569                     cls : 'dropdown-menu'
20570                 }
20571             ]
20572             
20573         };
20574         
20575         if(this.pos == 'top'){
20576             cfg.cls += ' dropup';
20577         }
20578         
20579         if(this.isSubMenu){
20580             cfg = {
20581                 tag : 'ul',
20582                 cls : 'dropdown-menu'
20583             }
20584         }
20585         
20586         return cfg;
20587     },
20588     
20589     onRender : function(ct, position)
20590     {
20591         this.isSubMenu = ct.hasClass('dropdown-submenu');
20592         
20593         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
20594     },
20595     
20596     initEvents : function() 
20597     {
20598         if(this.isSubMenu){
20599             return;
20600         }
20601         
20602         this.hidden = true;
20603         
20604         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
20605         this.triggerEl.on('click', this.onTriggerPress, this);
20606         
20607         this.buttonEl = this.el.select('button.dropdown-button', true).first();
20608         this.buttonEl.on('click', this.onClick, this);
20609         
20610     },
20611     
20612     list : function()
20613     {
20614         if(this.isSubMenu){
20615             return this.el;
20616         }
20617         
20618         return this.el.select('ul.dropdown-menu', true).first();
20619     },
20620     
20621     onClick : function(e)
20622     {
20623         this.fireEvent("click", this, e);
20624     },
20625     
20626     onTriggerPress  : function(e)
20627     {   
20628         if (this.isVisible()) {
20629             this.hide();
20630         } else {
20631             this.show();
20632         }
20633     },
20634     
20635     isVisible : function(){
20636         return !this.hidden;
20637     },
20638     
20639     show : function()
20640     {
20641         this.fireEvent("beforeshow", this);
20642         
20643         this.hidden = false;
20644         this.el.addClass('open');
20645         
20646         Roo.get(document).on("mouseup", this.onMouseUp, this);
20647         
20648         this.fireEvent("show", this);
20649         
20650         
20651     },
20652     
20653     hide : function()
20654     {
20655         this.fireEvent("beforehide", this);
20656         
20657         this.hidden = true;
20658         this.el.removeClass('open');
20659         
20660         Roo.get(document).un("mouseup", this.onMouseUp);
20661         
20662         this.fireEvent("hide", this);
20663     },
20664     
20665     onMouseUp : function()
20666     {
20667         this.hide();
20668     }
20669     
20670 });
20671
20672  
20673  /*
20674  * - LGPL
20675  *
20676  * menu item
20677  * 
20678  */
20679 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20680
20681 /**
20682  * @class Roo.bootstrap.menu.Item
20683  * @extends Roo.bootstrap.Component
20684  * Bootstrap MenuItem class
20685  * @cfg {Boolean} submenu (true | false) default false
20686  * @cfg {String} html text of the item
20687  * @cfg {String} href the link
20688  * @cfg {Boolean} disable (true | false) default false
20689  * @cfg {Boolean} preventDefault (true | false) default true
20690  * @cfg {String} icon Font awesome icon
20691  * @cfg {String} pos Submenu align to (left | right) default right 
20692  * 
20693  * 
20694  * @constructor
20695  * Create a new Item
20696  * @param {Object} config The config object
20697  */
20698
20699
20700 Roo.bootstrap.menu.Item = function(config){
20701     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
20702     this.addEvents({
20703         /**
20704          * @event mouseover
20705          * Fires when the mouse is hovering over this menu
20706          * @param {Roo.bootstrap.menu.Item} this
20707          * @param {Roo.EventObject} e
20708          */
20709         mouseover : true,
20710         /**
20711          * @event mouseout
20712          * Fires when the mouse exits this menu
20713          * @param {Roo.bootstrap.menu.Item} this
20714          * @param {Roo.EventObject} e
20715          */
20716         mouseout : true,
20717         // raw events
20718         /**
20719          * @event click
20720          * The raw click event for the entire grid.
20721          * @param {Roo.EventObject} e
20722          */
20723         click : true
20724     });
20725 };
20726
20727 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
20728     
20729     submenu : false,
20730     href : '',
20731     html : '',
20732     preventDefault: true,
20733     disable : false,
20734     icon : false,
20735     pos : 'right',
20736     
20737     getAutoCreate : function()
20738     {
20739         var text = [
20740             {
20741                 tag : 'span',
20742                 cls : 'roo-menu-item-text',
20743                 html : this.html
20744             }
20745         ];
20746         
20747         if(this.icon){
20748             text.unshift({
20749                 tag : 'i',
20750                 cls : 'fa ' + this.icon
20751             })
20752         }
20753         
20754         var cfg = {
20755             tag : 'li',
20756             cn : [
20757                 {
20758                     tag : 'a',
20759                     href : this.href || '#',
20760                     cn : text
20761                 }
20762             ]
20763         };
20764         
20765         if(this.disable){
20766             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
20767         }
20768         
20769         if(this.submenu){
20770             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
20771             
20772             if(this.pos == 'left'){
20773                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
20774             }
20775         }
20776         
20777         return cfg;
20778     },
20779     
20780     initEvents : function() 
20781     {
20782         this.el.on('mouseover', this.onMouseOver, this);
20783         this.el.on('mouseout', this.onMouseOut, this);
20784         
20785         this.el.select('a', true).first().on('click', this.onClick, this);
20786         
20787     },
20788     
20789     onClick : function(e)
20790     {
20791         if(this.preventDefault){
20792             e.preventDefault();
20793         }
20794         
20795         this.fireEvent("click", this, e);
20796     },
20797     
20798     onMouseOver : function(e)
20799     {
20800         if(this.submenu && this.pos == 'left'){
20801             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
20802         }
20803         
20804         this.fireEvent("mouseover", this, e);
20805     },
20806     
20807     onMouseOut : function(e)
20808     {
20809         this.fireEvent("mouseout", this, e);
20810     }
20811 });
20812
20813  
20814
20815  /*
20816  * - LGPL
20817  *
20818  * menu separator
20819  * 
20820  */
20821 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20822
20823 /**
20824  * @class Roo.bootstrap.menu.Separator
20825  * @extends Roo.bootstrap.Component
20826  * Bootstrap Separator class
20827  * 
20828  * @constructor
20829  * Create a new Separator
20830  * @param {Object} config The config object
20831  */
20832
20833
20834 Roo.bootstrap.menu.Separator = function(config){
20835     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
20836 };
20837
20838 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
20839     
20840     getAutoCreate : function(){
20841         var cfg = {
20842             tag : 'li',
20843             cls: 'divider'
20844         };
20845         
20846         return cfg;
20847     }
20848    
20849 });
20850
20851  
20852
20853  /*
20854  * - LGPL
20855  *
20856  * Tooltip
20857  * 
20858  */
20859
20860 /**
20861  * @class Roo.bootstrap.Tooltip
20862  * Bootstrap Tooltip class
20863  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
20864  * to determine which dom element triggers the tooltip.
20865  * 
20866  * It needs to add support for additional attributes like tooltip-position
20867  * 
20868  * @constructor
20869  * Create a new Toolti
20870  * @param {Object} config The config object
20871  */
20872
20873 Roo.bootstrap.Tooltip = function(config){
20874     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
20875 };
20876
20877 Roo.apply(Roo.bootstrap.Tooltip, {
20878     /**
20879      * @function init initialize tooltip monitoring.
20880      * @static
20881      */
20882     currentEl : false,
20883     currentTip : false,
20884     currentRegion : false,
20885     
20886     //  init : delay?
20887     
20888     init : function()
20889     {
20890         Roo.get(document).on('mouseover', this.enter ,this);
20891         Roo.get(document).on('mouseout', this.leave, this);
20892          
20893         
20894         this.currentTip = new Roo.bootstrap.Tooltip();
20895     },
20896     
20897     enter : function(ev)
20898     {
20899         var dom = ev.getTarget();
20900         //Roo.log(['enter',dom]);
20901         var el = Roo.fly(dom);
20902         if (this.currentEl) {
20903             //Roo.log(dom);
20904             //Roo.log(this.currentEl);
20905             //Roo.log(this.currentEl.contains(dom));
20906             if (this.currentEl == el) {
20907                 return;
20908             }
20909             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
20910                 return;
20911             }
20912
20913         }
20914         
20915         
20916         
20917         if (this.currentTip.el) {
20918             this.currentTip.el.hide(); // force hiding...
20919         }    
20920         //Roo.log(el);
20921         if (!el.attr('tooltip')) { // parents who have tip?
20922             return;
20923         }
20924         this.currentEl = el;
20925         this.currentTip.bind(el);
20926         this.currentRegion = Roo.lib.Region.getRegion(dom);
20927         this.currentTip.enter();
20928         
20929     },
20930     leave : function(ev)
20931     {
20932         var dom = ev.getTarget();
20933         //Roo.log(['leave',dom]);
20934         if (!this.currentEl) {
20935             return;
20936         }
20937         
20938         
20939         if (dom != this.currentEl.dom) {
20940             return;
20941         }
20942         var xy = ev.getXY();
20943         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
20944             return;
20945         }
20946         // only activate leave if mouse cursor is outside... bounding box..
20947         
20948         
20949         
20950         
20951         if (this.currentTip) {
20952             this.currentTip.leave();
20953         }
20954         //Roo.log('clear currentEl');
20955         this.currentEl = false;
20956         
20957         
20958     },
20959     alignment : {
20960         'left' : ['r-l', [-2,0], 'right'],
20961         'right' : ['l-r', [2,0], 'left'],
20962         'bottom' : ['t-b', [0,2], 'top'],
20963         'top' : [ 'b-t', [0,-2], 'bottom']
20964     }
20965     
20966 });
20967
20968
20969 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
20970     
20971     
20972     bindEl : false,
20973     
20974     delay : null, // can be { show : 300 , hide: 500}
20975     
20976     timeout : null,
20977     
20978     hoverState : null, //???
20979     
20980     placement : 'bottom', 
20981     
20982     getAutoCreate : function(){
20983     
20984         var cfg = {
20985            cls : 'tooltip',
20986            role : 'tooltip',
20987            cn : [
20988                 {
20989                     cls : 'tooltip-arrow'
20990                 },
20991                 {
20992                     cls : 'tooltip-inner'
20993                 }
20994            ]
20995         };
20996         
20997         return cfg;
20998     },
20999     bind : function(el)
21000     {
21001         this.bindEl = el;
21002     },
21003       
21004     
21005     enter : function () {
21006        
21007         if (this.timeout != null) {
21008             clearTimeout(this.timeout);
21009         }
21010         
21011         this.hoverState = 'in'
21012          //Roo.log("enter - show");
21013         if (!this.delay || !this.delay.show) {
21014             this.show();
21015             return;
21016         }
21017         var _t = this;
21018         this.timeout = setTimeout(function () {
21019             if (_t.hoverState == 'in') {
21020                 _t.show();
21021             }
21022         }, this.delay.show);
21023     },
21024     leave : function()
21025     {
21026         clearTimeout(this.timeout);
21027     
21028         this.hoverState = 'out'
21029          if (!this.delay || !this.delay.hide) {
21030             this.hide();
21031             return 
21032         }
21033        
21034         var _t = this;
21035         this.timeout = setTimeout(function () {
21036             //Roo.log("leave - timeout");
21037             
21038             if (_t.hoverState == 'out') {
21039                 _t.hide();
21040                 Roo.bootstrap.Tooltip.currentEl = false;
21041             }
21042         }, delay)
21043     },
21044     
21045     show : function ()
21046     {
21047         if (!this.el) {
21048             this.render(document.body);
21049         }
21050         // set content.
21051         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
21052         this.el.select('.tooltip-inner',true).first().dom.innerHTML = this.bindEl.attr('tooltip');
21053         
21054         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
21055         
21056         var placement = typeof this.placement == 'function' ?
21057             this.placement.call(this, this.el, on_el) :
21058             this.placement;
21059             
21060         var autoToken = /\s?auto?\s?/i;
21061         var autoPlace = autoToken.test(placement);
21062         if (autoPlace) {
21063             placement = placement.replace(autoToken, '') || 'top';
21064         }
21065         
21066         //this.el.detach()
21067         //this.el.setXY([0,0]);
21068         this.el.show();
21069         //this.el.dom.style.display='block';
21070         this.el.addClass(placement);
21071         
21072         //this.el.appendTo(on_el);
21073         
21074         var p = this.getPosition();
21075         var box = this.el.getBox();
21076         
21077         if (autoPlace) {
21078             // fixme..
21079         }
21080         var align = Roo.bootstrap.Tooltip.alignment[placement]
21081         this.el.alignTo(this.bindEl, align[0],align[1]);
21082         //var arrow = this.el.select('.arrow',true).first();
21083         //arrow.set(align[2], 
21084         
21085         this.el.addClass('in fade');
21086         this.hoverState = null;
21087         
21088         if (this.el.hasClass('fade')) {
21089             // fade it?
21090         }
21091         
21092     },
21093     hide : function()
21094     {
21095          
21096         if (!this.el) {
21097             return;
21098         }
21099         //this.el.setXY([0,0]);
21100         this.el.removeClass('in');
21101         //this.el.hide();
21102         
21103     }
21104     
21105 });
21106  
21107
21108  /*
21109  * - LGPL
21110  *
21111  * Location Picker
21112  * 
21113  */
21114
21115 /**
21116  * @class Roo.bootstrap.LocationPicker
21117  * @extends Roo.bootstrap.Component
21118  * Bootstrap LocationPicker class
21119  * @cfg {Number} latitude Position when init default 0
21120  * @cfg {Number} longitude Position when init default 0
21121  * @cfg {Number} zoom default 15
21122  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
21123  * @cfg {Boolean} mapTypeControl default false
21124  * @cfg {Boolean} disableDoubleClickZoom default false
21125  * @cfg {Boolean} scrollwheel default true
21126  * @cfg {Boolean} streetViewControl default false
21127  * @cfg {Number} radius default 0
21128  * @cfg {String} locationName
21129  * @cfg {Boolean} draggable default true
21130  * @cfg {Boolean} enableAutocomplete default false
21131  * @cfg {Boolean} enableReverseGeocode default true
21132  * @cfg {String} markerTitle
21133  * 
21134  * @constructor
21135  * Create a new LocationPicker
21136  * @param {Object} config The config object
21137  */
21138
21139
21140 Roo.bootstrap.LocationPicker = function(config){
21141     
21142     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
21143     
21144      this.addEvents({
21145             /**
21146              * @event initial
21147              * Fires when the picker initialized.
21148              * @param {Roo.bootstrap.LocationPicker} this
21149              * @param {Google Location} location
21150              */
21151             initial : true,
21152             /**
21153              * @event positionchanged
21154              * Fires when the picker position changed.
21155              * @param {Roo.bootstrap.LocationPicker} this
21156              * @param {Google Location} location
21157              */
21158             positionchanged : true,
21159             /**
21160              * @event resize
21161              * Fires when the map resize.
21162              * @param {Roo.bootstrap.LocationPicker} this
21163              */
21164             resize : true,
21165             /**
21166              * @event show
21167              * Fires when the map show.
21168              * @param {Roo.bootstrap.LocationPicker} this
21169              */
21170             show : true,
21171             /**
21172              * @event hide
21173              * Fires when the map hide.
21174              * @param {Roo.bootstrap.LocationPicker} this
21175              */
21176             hide : true
21177         });
21178         
21179 };
21180
21181 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
21182     
21183     gMapContext: false,
21184     
21185     latitude: 0,
21186     longitude: 0,
21187     zoom: 15,
21188     mapTypeId: false,
21189     mapTypeControl: false,
21190     disableDoubleClickZoom: false,
21191     scrollwheel: true,
21192     streetViewControl: false,
21193     radius: 0,
21194     locationName: '',
21195     draggable: true,
21196     enableAutocomplete: false,
21197     enableReverseGeocode: true,
21198     markerTitle: '',
21199     
21200     getAutoCreate: function()
21201     {
21202
21203         var cfg = {
21204             tag: 'div',
21205             cls: 'roo-location-picker'
21206         };
21207         
21208         return cfg
21209     },
21210     
21211     initEvents: function(ct, position)
21212     {   
21213         if(!this.mapTypeId){
21214             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
21215         }
21216             
21217         if(!this.el.getWidth() || this.isApplied()){
21218             return;
21219         }
21220         
21221         this.el.setVisibilityMode(Roo.Element.DISPLAY);
21222         
21223         this.initial();
21224     },
21225     
21226     initial: function()
21227     {
21228         this.gMapContext = this.GMapContext();
21229         
21230         var _this = this;
21231         
21232         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
21233             _this.setPosition(_this.gMapContext.marker.position);
21234         });
21235         
21236         this.setPosition(this.gMapContext.location);
21237         
21238         this.fireEvent('initial', this, this.gMapContext.location);
21239     },
21240     
21241     isApplied: function() 
21242     {
21243         return this.getGmapContext() == false ? false : true;
21244     },
21245     
21246     getGmapContext: function() 
21247     {
21248         return this.gMapContext
21249     },
21250     
21251     GMapContext: function() 
21252     {
21253         var _map = new google.maps.Map(this.el.dom, this);
21254         var _marker = new google.maps.Marker({
21255             position: new google.maps.LatLng(this.latitude, this.longitude),
21256             map: _map,
21257             title: this.markerTitle,
21258             draggable: this.draggable
21259         });
21260         
21261         return {
21262             map: _map,
21263             marker: _marker,
21264             circle: null,
21265             location: _marker.position,
21266             radius: this.radius,
21267             locationName: this.locationName,
21268             addressComponents: {
21269                 formatted_address: null,
21270                 addressLine1: null,
21271                 addressLine2: null,
21272                 streetName: null,
21273                 streetNumber: null,
21274                 city: null,
21275                 district: null,
21276                 state: null,
21277                 stateOrProvince: null
21278             },
21279             settings: this,
21280             domContainer: this.el.dom,
21281             geodecoder: new google.maps.Geocoder()
21282         };
21283     },
21284     
21285     drawCircle: function(center, radius, options) 
21286     {
21287         if (this.gMapContext.circle != null) {
21288             this.gMapContext.circle.setMap(null);
21289         }
21290         if (radius > 0) {
21291             radius *= 1;
21292             options = Roo.apply({}, options, {
21293                 strokeColor: "#0000FF",
21294                 strokeOpacity: .35,
21295                 strokeWeight: 2,
21296                 fillColor: "#0000FF",
21297                 fillOpacity: .2
21298             });
21299             
21300             options.map = this.gMapContext.map;
21301             options.radius = radius;
21302             options.center = center;
21303             this.gMapContext.circle = new google.maps.Circle(options);
21304             return this.gMapContext.circle;
21305         }
21306         
21307         return null;
21308     },
21309     
21310     setPosition: function(location) 
21311     {
21312         this.gMapContext.location = location;
21313         this.gMapContext.marker.setPosition(location);
21314         this.gMapContext.map.panTo(location);
21315         this.drawCircle(location, this.gMapContext.radius, {});
21316         
21317         var _this = this;
21318         
21319         if (this.gMapContext.settings.enableReverseGeocode) {
21320             this.gMapContext.geodecoder.geocode({
21321                 latLng: this.gMapContext.location
21322             }, function(results, status) {
21323                 
21324                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
21325                     _this.gMapContext.locationName = results[0].formatted_address;
21326                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
21327                     
21328                     _this.fireEvent('positionchanged', this, location);
21329                 }
21330             });
21331             
21332             return;
21333         }
21334         
21335         this.fireEvent('positionchanged', this, location);
21336     },
21337     
21338     resize: function()
21339     {
21340         google.maps.event.trigger(this.gMapContext.map, "resize");
21341         
21342         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
21343         
21344         this.fireEvent('resize', this);
21345     },
21346     
21347     setPositionByLatLng: function(latitude, longitude)
21348     {
21349         this.setPosition(new google.maps.LatLng(latitude, longitude));
21350     },
21351     
21352     getCurrentPosition: function() 
21353     {
21354         return {
21355             latitude: this.gMapContext.location.lat(),
21356             longitude: this.gMapContext.location.lng()
21357         };
21358     },
21359     
21360     getAddressName: function() 
21361     {
21362         return this.gMapContext.locationName;
21363     },
21364     
21365     getAddressComponents: function() 
21366     {
21367         return this.gMapContext.addressComponents;
21368     },
21369     
21370     address_component_from_google_geocode: function(address_components) 
21371     {
21372         var result = {};
21373         
21374         for (var i = 0; i < address_components.length; i++) {
21375             var component = address_components[i];
21376             if (component.types.indexOf("postal_code") >= 0) {
21377                 result.postalCode = component.short_name;
21378             } else if (component.types.indexOf("street_number") >= 0) {
21379                 result.streetNumber = component.short_name;
21380             } else if (component.types.indexOf("route") >= 0) {
21381                 result.streetName = component.short_name;
21382             } else if (component.types.indexOf("neighborhood") >= 0) {
21383                 result.city = component.short_name;
21384             } else if (component.types.indexOf("locality") >= 0) {
21385                 result.city = component.short_name;
21386             } else if (component.types.indexOf("sublocality") >= 0) {
21387                 result.district = component.short_name;
21388             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
21389                 result.stateOrProvince = component.short_name;
21390             } else if (component.types.indexOf("country") >= 0) {
21391                 result.country = component.short_name;
21392             }
21393         }
21394         
21395         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
21396         result.addressLine2 = "";
21397         return result;
21398     },
21399     
21400     setZoomLevel: function(zoom)
21401     {
21402         this.gMapContext.map.setZoom(zoom);
21403     },
21404     
21405     show: function()
21406     {
21407         if(!this.el){
21408             return;
21409         }
21410         
21411         this.el.show();
21412         
21413         this.resize();
21414         
21415         this.fireEvent('show', this);
21416     },
21417     
21418     hide: function()
21419     {
21420         if(!this.el){
21421             return;
21422         }
21423         
21424         this.el.hide();
21425         
21426         this.fireEvent('hide', this);
21427     }
21428     
21429 });
21430 /*
21431  * - LGPL
21432  *
21433  * Alert
21434  * 
21435  */
21436
21437 /**
21438  * @class Roo.bootstrap.Alert
21439  * @extends Roo.bootstrap.Component
21440  * Bootstrap Alert class
21441  * @cfg {String} title The title of alert
21442  * @cfg {String} html The content of alert
21443  * @cfg {String} weight (  success | info | warning | danger )
21444  * @cfg {String} faicon font-awesomeicon
21445  * 
21446  * @constructor
21447  * Create a new alert
21448  * @param {Object} config The config object
21449  */
21450
21451
21452 Roo.bootstrap.Alert = function(config){
21453     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
21454     
21455 };
21456
21457 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
21458     
21459     title: '',
21460     html: '',
21461     weight: false,
21462     faicon: false,
21463     
21464     getAutoCreate : function()
21465     {
21466         
21467         var cfg = {
21468             tag : 'div',
21469             cls : 'alert',
21470             cn : [
21471                 {
21472                     tag : 'i',
21473                     cls : 'roo-alert-icon'
21474                     
21475                 },
21476                 {
21477                     tag : 'b',
21478                     cls : 'roo-alert-title',
21479                     html : this.title
21480                 },
21481                 {
21482                     tag : 'span',
21483                     cls : 'roo-alert-text',
21484                     html : this.html
21485                 }
21486             ]
21487         };
21488         
21489         if(this.faicon){
21490             cfg.cn[0].cls += ' fa ' + this.faicon;
21491         }
21492         
21493         if(this.weight){
21494             cfg.cls += ' alert-' + this.weight;
21495         }
21496         
21497         return cfg;
21498     },
21499     
21500     initEvents: function() 
21501     {
21502         this.el.setVisibilityMode(Roo.Element.DISPLAY);
21503     },
21504     
21505     setTitle : function(str)
21506     {
21507         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
21508     },
21509     
21510     setText : function(str)
21511     {
21512         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
21513     },
21514     
21515     setWeight : function(weight)
21516     {
21517         if(this.weight){
21518             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
21519         }
21520         
21521         this.weight = weight;
21522         
21523         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
21524     },
21525     
21526     setIcon : function(icon)
21527     {
21528         if(this.faicon){
21529             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
21530         }
21531         
21532         this.faicon = icon
21533         
21534         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
21535     },
21536     
21537     hide: function() 
21538     {
21539         this.el.hide();   
21540     },
21541     
21542     show: function() 
21543     {  
21544         this.el.show();   
21545     }
21546     
21547 });
21548
21549