css/date.css
[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  * 
4166  * 
4167  * @constructor
4168  * Create a new PaginationItem
4169  * @param {Object} config The config object
4170  */
4171
4172
4173 Roo.bootstrap.PaginationItem = function(config){
4174     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4175     this.addEvents({
4176         // raw events
4177         /**
4178          * @event click
4179          * The raw click event for the entire grid.
4180          * @param {Roo.EventObject} e
4181          */
4182         "click" : true
4183     });
4184 };
4185
4186 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4187     
4188     href : false,
4189     html : false,
4190     preventDefault: true,
4191     active : false,
4192     cls : false,
4193     
4194     getAutoCreate : function(){
4195         var cfg= {
4196             tag: 'li',
4197             cn: [
4198                 {
4199                     tag : 'a',
4200                     href : this.href ? this.href : '#',
4201                     html : this.html ? this.html : ''
4202                 }
4203             ]
4204         };
4205         
4206         if(this.cls){
4207             cfg.cls = this.cls;
4208         }
4209         
4210         if(this.active){
4211             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4212         }
4213         
4214         return cfg;
4215     },
4216     
4217     initEvents: function() {
4218         
4219         this.el.on('click', this.onClick, this);
4220         
4221     },
4222     onClick : function(e)
4223     {
4224         Roo.log('PaginationItem on click ');
4225         if(this.preventDefault){
4226             e.preventDefault();
4227         }
4228         
4229         this.fireEvent('click', this, e);
4230     }
4231    
4232 });
4233
4234  
4235
4236  /*
4237  * - LGPL
4238  *
4239  * slider
4240  * 
4241  */
4242
4243
4244 /**
4245  * @class Roo.bootstrap.Slider
4246  * @extends Roo.bootstrap.Component
4247  * Bootstrap Slider class
4248  *    
4249  * @constructor
4250  * Create a new Slider
4251  * @param {Object} config The config object
4252  */
4253
4254 Roo.bootstrap.Slider = function(config){
4255     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4256 };
4257
4258 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
4259     
4260     getAutoCreate : function(){
4261         
4262         var cfg = {
4263             tag: 'div',
4264             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4265             cn: [
4266                 {
4267                     tag: 'a',
4268                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
4269                 }
4270             ]
4271         }
4272         
4273         return cfg;
4274     }
4275    
4276 });
4277
4278  /*
4279  * Based on:
4280  * Ext JS Library 1.1.1
4281  * Copyright(c) 2006-2007, Ext JS, LLC.
4282  *
4283  * Originally Released Under LGPL - original licence link has changed is not relivant.
4284  *
4285  * Fork - LGPL
4286  * <script type="text/javascript">
4287  */
4288  
4289
4290 /**
4291  * @class Roo.grid.ColumnModel
4292  * @extends Roo.util.Observable
4293  * This is the default implementation of a ColumnModel used by the Grid. It defines
4294  * the columns in the grid.
4295  * <br>Usage:<br>
4296  <pre><code>
4297  var colModel = new Roo.grid.ColumnModel([
4298         {header: "Ticker", width: 60, sortable: true, locked: true},
4299         {header: "Company Name", width: 150, sortable: true},
4300         {header: "Market Cap.", width: 100, sortable: true},
4301         {header: "$ Sales", width: 100, sortable: true, renderer: money},
4302         {header: "Employees", width: 100, sortable: true, resizable: false}
4303  ]);
4304  </code></pre>
4305  * <p>
4306  
4307  * The config options listed for this class are options which may appear in each
4308  * individual column definition.
4309  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4310  * @constructor
4311  * @param {Object} config An Array of column config objects. See this class's
4312  * config objects for details.
4313 */
4314 Roo.grid.ColumnModel = function(config){
4315         /**
4316      * The config passed into the constructor
4317      */
4318     this.config = config;
4319     this.lookup = {};
4320
4321     // if no id, create one
4322     // if the column does not have a dataIndex mapping,
4323     // map it to the order it is in the config
4324     for(var i = 0, len = config.length; i < len; i++){
4325         var c = config[i];
4326         if(typeof c.dataIndex == "undefined"){
4327             c.dataIndex = i;
4328         }
4329         if(typeof c.renderer == "string"){
4330             c.renderer = Roo.util.Format[c.renderer];
4331         }
4332         if(typeof c.id == "undefined"){
4333             c.id = Roo.id();
4334         }
4335         if(c.editor && c.editor.xtype){
4336             c.editor  = Roo.factory(c.editor, Roo.grid);
4337         }
4338         if(c.editor && c.editor.isFormField){
4339             c.editor = new Roo.grid.GridEditor(c.editor);
4340         }
4341         this.lookup[c.id] = c;
4342     }
4343
4344     /**
4345      * The width of columns which have no width specified (defaults to 100)
4346      * @type Number
4347      */
4348     this.defaultWidth = 100;
4349
4350     /**
4351      * Default sortable of columns which have no sortable specified (defaults to false)
4352      * @type Boolean
4353      */
4354     this.defaultSortable = false;
4355
4356     this.addEvents({
4357         /**
4358              * @event widthchange
4359              * Fires when the width of a column changes.
4360              * @param {ColumnModel} this
4361              * @param {Number} columnIndex The column index
4362              * @param {Number} newWidth The new width
4363              */
4364             "widthchange": true,
4365         /**
4366              * @event headerchange
4367              * Fires when the text of a header changes.
4368              * @param {ColumnModel} this
4369              * @param {Number} columnIndex The column index
4370              * @param {Number} newText The new header text
4371              */
4372             "headerchange": true,
4373         /**
4374              * @event hiddenchange
4375              * Fires when a column is hidden or "unhidden".
4376              * @param {ColumnModel} this
4377              * @param {Number} columnIndex The column index
4378              * @param {Boolean} hidden true if hidden, false otherwise
4379              */
4380             "hiddenchange": true,
4381             /**
4382          * @event columnmoved
4383          * Fires when a column is moved.
4384          * @param {ColumnModel} this
4385          * @param {Number} oldIndex
4386          * @param {Number} newIndex
4387          */
4388         "columnmoved" : true,
4389         /**
4390          * @event columlockchange
4391          * Fires when a column's locked state is changed
4392          * @param {ColumnModel} this
4393          * @param {Number} colIndex
4394          * @param {Boolean} locked true if locked
4395          */
4396         "columnlockchange" : true
4397     });
4398     Roo.grid.ColumnModel.superclass.constructor.call(this);
4399 };
4400 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4401     /**
4402      * @cfg {String} header The header text to display in the Grid view.
4403      */
4404     /**
4405      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4406      * {@link Roo.data.Record} definition from which to draw the column's value. If not
4407      * specified, the column's index is used as an index into the Record's data Array.
4408      */
4409     /**
4410      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4411      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4412      */
4413     /**
4414      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4415      * Defaults to the value of the {@link #defaultSortable} property.
4416      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4417      */
4418     /**
4419      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
4420      */
4421     /**
4422      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
4423      */
4424     /**
4425      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4426      */
4427     /**
4428      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4429      */
4430     /**
4431      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4432      * given the cell's data value. See {@link #setRenderer}. If not specified, the
4433      * default renderer uses the raw data value. If an object is returned (bootstrap only)
4434      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4435      */
4436        /**
4437      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
4438      */
4439     /**
4440      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
4441      */
4442     /**
4443      * @cfg {String} cursor (Optional)
4444      */
4445     /**
4446      * Returns the id of the column at the specified index.
4447      * @param {Number} index The column index
4448      * @return {String} the id
4449      */
4450     getColumnId : function(index){
4451         return this.config[index].id;
4452     },
4453
4454     /**
4455      * Returns the column for a specified id.
4456      * @param {String} id The column id
4457      * @return {Object} the column
4458      */
4459     getColumnById : function(id){
4460         return this.lookup[id];
4461     },
4462
4463     
4464     /**
4465      * Returns the column for a specified dataIndex.
4466      * @param {String} dataIndex The column dataIndex
4467      * @return {Object|Boolean} the column or false if not found
4468      */
4469     getColumnByDataIndex: function(dataIndex){
4470         var index = this.findColumnIndex(dataIndex);
4471         return index > -1 ? this.config[index] : false;
4472     },
4473     
4474     /**
4475      * Returns the index for a specified column id.
4476      * @param {String} id The column id
4477      * @return {Number} the index, or -1 if not found
4478      */
4479     getIndexById : function(id){
4480         for(var i = 0, len = this.config.length; i < len; i++){
4481             if(this.config[i].id == id){
4482                 return i;
4483             }
4484         }
4485         return -1;
4486     },
4487     
4488     /**
4489      * Returns the index for a specified column dataIndex.
4490      * @param {String} dataIndex The column dataIndex
4491      * @return {Number} the index, or -1 if not found
4492      */
4493     
4494     findColumnIndex : function(dataIndex){
4495         for(var i = 0, len = this.config.length; i < len; i++){
4496             if(this.config[i].dataIndex == dataIndex){
4497                 return i;
4498             }
4499         }
4500         return -1;
4501     },
4502     
4503     
4504     moveColumn : function(oldIndex, newIndex){
4505         var c = this.config[oldIndex];
4506         this.config.splice(oldIndex, 1);
4507         this.config.splice(newIndex, 0, c);
4508         this.dataMap = null;
4509         this.fireEvent("columnmoved", this, oldIndex, newIndex);
4510     },
4511
4512     isLocked : function(colIndex){
4513         return this.config[colIndex].locked === true;
4514     },
4515
4516     setLocked : function(colIndex, value, suppressEvent){
4517         if(this.isLocked(colIndex) == value){
4518             return;
4519         }
4520         this.config[colIndex].locked = value;
4521         if(!suppressEvent){
4522             this.fireEvent("columnlockchange", this, colIndex, value);
4523         }
4524     },
4525
4526     getTotalLockedWidth : function(){
4527         var totalWidth = 0;
4528         for(var i = 0; i < this.config.length; i++){
4529             if(this.isLocked(i) && !this.isHidden(i)){
4530                 this.totalWidth += this.getColumnWidth(i);
4531             }
4532         }
4533         return totalWidth;
4534     },
4535
4536     getLockedCount : function(){
4537         for(var i = 0, len = this.config.length; i < len; i++){
4538             if(!this.isLocked(i)){
4539                 return i;
4540             }
4541         }
4542     },
4543
4544     /**
4545      * Returns the number of columns.
4546      * @return {Number}
4547      */
4548     getColumnCount : function(visibleOnly){
4549         if(visibleOnly === true){
4550             var c = 0;
4551             for(var i = 0, len = this.config.length; i < len; i++){
4552                 if(!this.isHidden(i)){
4553                     c++;
4554                 }
4555             }
4556             return c;
4557         }
4558         return this.config.length;
4559     },
4560
4561     /**
4562      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
4563      * @param {Function} fn
4564      * @param {Object} scope (optional)
4565      * @return {Array} result
4566      */
4567     getColumnsBy : function(fn, scope){
4568         var r = [];
4569         for(var i = 0, len = this.config.length; i < len; i++){
4570             var c = this.config[i];
4571             if(fn.call(scope||this, c, i) === true){
4572                 r[r.length] = c;
4573             }
4574         }
4575         return r;
4576     },
4577
4578     /**
4579      * Returns true if the specified column is sortable.
4580      * @param {Number} col The column index
4581      * @return {Boolean}
4582      */
4583     isSortable : function(col){
4584         if(typeof this.config[col].sortable == "undefined"){
4585             return this.defaultSortable;
4586         }
4587         return this.config[col].sortable;
4588     },
4589
4590     /**
4591      * Returns the rendering (formatting) function defined for the column.
4592      * @param {Number} col The column index.
4593      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
4594      */
4595     getRenderer : function(col){
4596         if(!this.config[col].renderer){
4597             return Roo.grid.ColumnModel.defaultRenderer;
4598         }
4599         return this.config[col].renderer;
4600     },
4601
4602     /**
4603      * Sets the rendering (formatting) function for a column.
4604      * @param {Number} col The column index
4605      * @param {Function} fn The function to use to process the cell's raw data
4606      * to return HTML markup for the grid view. The render function is called with
4607      * the following parameters:<ul>
4608      * <li>Data value.</li>
4609      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
4610      * <li>css A CSS style string to apply to the table cell.</li>
4611      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
4612      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
4613      * <li>Row index</li>
4614      * <li>Column index</li>
4615      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
4616      */
4617     setRenderer : function(col, fn){
4618         this.config[col].renderer = fn;
4619     },
4620
4621     /**
4622      * Returns the width for the specified column.
4623      * @param {Number} col The column index
4624      * @return {Number}
4625      */
4626     getColumnWidth : function(col){
4627         return this.config[col].width * 1 || this.defaultWidth;
4628     },
4629
4630     /**
4631      * Sets the width for a column.
4632      * @param {Number} col The column index
4633      * @param {Number} width The new width
4634      */
4635     setColumnWidth : function(col, width, suppressEvent){
4636         this.config[col].width = width;
4637         this.totalWidth = null;
4638         if(!suppressEvent){
4639              this.fireEvent("widthchange", this, col, width);
4640         }
4641     },
4642
4643     /**
4644      * Returns the total width of all columns.
4645      * @param {Boolean} includeHidden True to include hidden column widths
4646      * @return {Number}
4647      */
4648     getTotalWidth : function(includeHidden){
4649         if(!this.totalWidth){
4650             this.totalWidth = 0;
4651             for(var i = 0, len = this.config.length; i < len; i++){
4652                 if(includeHidden || !this.isHidden(i)){
4653                     this.totalWidth += this.getColumnWidth(i);
4654                 }
4655             }
4656         }
4657         return this.totalWidth;
4658     },
4659
4660     /**
4661      * Returns the header for the specified column.
4662      * @param {Number} col The column index
4663      * @return {String}
4664      */
4665     getColumnHeader : function(col){
4666         return this.config[col].header;
4667     },
4668
4669     /**
4670      * Sets the header for a column.
4671      * @param {Number} col The column index
4672      * @param {String} header The new header
4673      */
4674     setColumnHeader : function(col, header){
4675         this.config[col].header = header;
4676         this.fireEvent("headerchange", this, col, header);
4677     },
4678
4679     /**
4680      * Returns the tooltip for the specified column.
4681      * @param {Number} col The column index
4682      * @return {String}
4683      */
4684     getColumnTooltip : function(col){
4685             return this.config[col].tooltip;
4686     },
4687     /**
4688      * Sets the tooltip for a column.
4689      * @param {Number} col The column index
4690      * @param {String} tooltip The new tooltip
4691      */
4692     setColumnTooltip : function(col, tooltip){
4693             this.config[col].tooltip = tooltip;
4694     },
4695
4696     /**
4697      * Returns the dataIndex for the specified column.
4698      * @param {Number} col The column index
4699      * @return {Number}
4700      */
4701     getDataIndex : function(col){
4702         return this.config[col].dataIndex;
4703     },
4704
4705     /**
4706      * Sets the dataIndex for a column.
4707      * @param {Number} col The column index
4708      * @param {Number} dataIndex The new dataIndex
4709      */
4710     setDataIndex : function(col, dataIndex){
4711         this.config[col].dataIndex = dataIndex;
4712     },
4713
4714     
4715     
4716     /**
4717      * Returns true if the cell is editable.
4718      * @param {Number} colIndex The column index
4719      * @param {Number} rowIndex The row index
4720      * @return {Boolean}
4721      */
4722     isCellEditable : function(colIndex, rowIndex){
4723         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
4724     },
4725
4726     /**
4727      * Returns the editor defined for the cell/column.
4728      * return false or null to disable editing.
4729      * @param {Number} colIndex The column index
4730      * @param {Number} rowIndex The row index
4731      * @return {Object}
4732      */
4733     getCellEditor : function(colIndex, rowIndex){
4734         return this.config[colIndex].editor;
4735     },
4736
4737     /**
4738      * Sets if a column is editable.
4739      * @param {Number} col The column index
4740      * @param {Boolean} editable True if the column is editable
4741      */
4742     setEditable : function(col, editable){
4743         this.config[col].editable = editable;
4744     },
4745
4746
4747     /**
4748      * Returns true if the column is hidden.
4749      * @param {Number} colIndex The column index
4750      * @return {Boolean}
4751      */
4752     isHidden : function(colIndex){
4753         return this.config[colIndex].hidden;
4754     },
4755
4756
4757     /**
4758      * Returns true if the column width cannot be changed
4759      */
4760     isFixed : function(colIndex){
4761         return this.config[colIndex].fixed;
4762     },
4763
4764     /**
4765      * Returns true if the column can be resized
4766      * @return {Boolean}
4767      */
4768     isResizable : function(colIndex){
4769         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
4770     },
4771     /**
4772      * Sets if a column is hidden.
4773      * @param {Number} colIndex The column index
4774      * @param {Boolean} hidden True if the column is hidden
4775      */
4776     setHidden : function(colIndex, hidden){
4777         this.config[colIndex].hidden = hidden;
4778         this.totalWidth = null;
4779         this.fireEvent("hiddenchange", this, colIndex, hidden);
4780     },
4781
4782     /**
4783      * Sets the editor for a column.
4784      * @param {Number} col The column index
4785      * @param {Object} editor The editor object
4786      */
4787     setEditor : function(col, editor){
4788         this.config[col].editor = editor;
4789     }
4790 });
4791
4792 Roo.grid.ColumnModel.defaultRenderer = function(value){
4793         if(typeof value == "string" && value.length < 1){
4794             return "&#160;";
4795         }
4796         return value;
4797 };
4798
4799 // Alias for backwards compatibility
4800 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
4801 /*
4802  * Based on:
4803  * Ext JS Library 1.1.1
4804  * Copyright(c) 2006-2007, Ext JS, LLC.
4805  *
4806  * Originally Released Under LGPL - original licence link has changed is not relivant.
4807  *
4808  * Fork - LGPL
4809  * <script type="text/javascript">
4810  */
4811  
4812 /**
4813  * @class Roo.LoadMask
4814  * A simple utility class for generically masking elements while loading data.  If the element being masked has
4815  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
4816  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
4817  * element's UpdateManager load indicator and will be destroyed after the initial load.
4818  * @constructor
4819  * Create a new LoadMask
4820  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
4821  * @param {Object} config The config object
4822  */
4823 Roo.LoadMask = function(el, config){
4824     this.el = Roo.get(el);
4825     Roo.apply(this, config);
4826     if(this.store){
4827         this.store.on('beforeload', this.onBeforeLoad, this);
4828         this.store.on('load', this.onLoad, this);
4829         this.store.on('loadexception', this.onLoadException, this);
4830         this.removeMask = false;
4831     }else{
4832         var um = this.el.getUpdateManager();
4833         um.showLoadIndicator = false; // disable the default indicator
4834         um.on('beforeupdate', this.onBeforeLoad, this);
4835         um.on('update', this.onLoad, this);
4836         um.on('failure', this.onLoad, this);
4837         this.removeMask = true;
4838     }
4839 };
4840
4841 Roo.LoadMask.prototype = {
4842     /**
4843      * @cfg {Boolean} removeMask
4844      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
4845      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
4846      */
4847     /**
4848      * @cfg {String} msg
4849      * The text to display in a centered loading message box (defaults to 'Loading...')
4850      */
4851     msg : 'Loading...',
4852     /**
4853      * @cfg {String} msgCls
4854      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
4855      */
4856     msgCls : 'x-mask-loading',
4857
4858     /**
4859      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
4860      * @type Boolean
4861      */
4862     disabled: false,
4863
4864     /**
4865      * Disables the mask to prevent it from being displayed
4866      */
4867     disable : function(){
4868        this.disabled = true;
4869     },
4870
4871     /**
4872      * Enables the mask so that it can be displayed
4873      */
4874     enable : function(){
4875         this.disabled = false;
4876     },
4877     
4878     onLoadException : function()
4879     {
4880         Roo.log(arguments);
4881         
4882         if (typeof(arguments[3]) != 'undefined') {
4883             Roo.MessageBox.alert("Error loading",arguments[3]);
4884         } 
4885         /*
4886         try {
4887             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
4888                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
4889             }   
4890         } catch(e) {
4891             
4892         }
4893         */
4894     
4895         
4896         
4897         this.el.unmask(this.removeMask);
4898     },
4899     // private
4900     onLoad : function()
4901     {
4902         this.el.unmask(this.removeMask);
4903     },
4904
4905     // private
4906     onBeforeLoad : function(){
4907         if(!this.disabled){
4908             this.el.mask(this.msg, this.msgCls);
4909         }
4910     },
4911
4912     // private
4913     destroy : function(){
4914         if(this.store){
4915             this.store.un('beforeload', this.onBeforeLoad, this);
4916             this.store.un('load', this.onLoad, this);
4917             this.store.un('loadexception', this.onLoadException, this);
4918         }else{
4919             var um = this.el.getUpdateManager();
4920             um.un('beforeupdate', this.onBeforeLoad, this);
4921             um.un('update', this.onLoad, this);
4922             um.un('failure', this.onLoad, this);
4923         }
4924     }
4925 };/*
4926  * - LGPL
4927  *
4928  * table
4929  * 
4930  */
4931
4932 /**
4933  * @class Roo.bootstrap.Table
4934  * @extends Roo.bootstrap.Component
4935  * Bootstrap Table class
4936  * @cfg {String} cls table class
4937  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
4938  * @cfg {String} bgcolor Specifies the background color for a table
4939  * @cfg {Number} border Specifies whether the table cells should have borders or not
4940  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
4941  * @cfg {Number} cellspacing Specifies the space between cells
4942  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
4943  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
4944  * @cfg {String} sortable Specifies that the table should be sortable
4945  * @cfg {String} summary Specifies a summary of the content of a table
4946  * @cfg {Number} width Specifies the width of a table
4947  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
4948  * 
4949  * @cfg {boolean} striped Should the rows be alternative striped
4950  * @cfg {boolean} bordered Add borders to the table
4951  * @cfg {boolean} hover Add hover highlighting
4952  * @cfg {boolean} condensed Format condensed
4953  * @cfg {boolean} responsive Format condensed
4954  * @cfg {Boolean} loadMask (true|false) default false
4955  * @cfg {Boolean} tfoot (true|false) generate tfoot, default true
4956  * @cfg {Boolean} thead (true|false) generate thead, default true
4957  * @cfg {Boolean} RowSelection (true|false) default false
4958  * @cfg {Boolean} CellSelection (true|false) default false
4959  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
4960  
4961  * 
4962  * @constructor
4963  * Create a new Table
4964  * @param {Object} config The config object
4965  */
4966
4967 Roo.bootstrap.Table = function(config){
4968     Roo.bootstrap.Table.superclass.constructor.call(this, config);
4969     
4970     if (this.sm) {
4971         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
4972         this.sm = this.selModel;
4973         this.sm.xmodule = this.xmodule || false;
4974     }
4975     if (this.cm && typeof(this.cm.config) == 'undefined') {
4976         this.colModel = new Roo.grid.ColumnModel(this.cm);
4977         this.cm = this.colModel;
4978         this.cm.xmodule = this.xmodule || false;
4979     }
4980     if (this.store) {
4981         this.store= Roo.factory(this.store, Roo.data);
4982         this.ds = this.store;
4983         this.ds.xmodule = this.xmodule || false;
4984          
4985     }
4986     if (this.footer && this.store) {
4987         this.footer.dataSource = this.ds;
4988         this.footer = Roo.factory(this.footer);
4989     }
4990     
4991     /** @private */
4992     this.addEvents({
4993         /**
4994          * @event cellclick
4995          * Fires when a cell is clicked
4996          * @param {Roo.bootstrap.Table} this
4997          * @param {Roo.Element} el
4998          * @param {Number} rowIndex
4999          * @param {Number} columnIndex
5000          * @param {Roo.EventObject} e
5001          */
5002         "cellclick" : true,
5003         /**
5004          * @event celldblclick
5005          * Fires when a cell is double 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         "celldblclick" : true,
5013         /**
5014          * @event rowclick
5015          * Fires when a row is clicked
5016          * @param {Roo.bootstrap.Table} this
5017          * @param {Roo.Element} el
5018          * @param {Number} rowIndex
5019          * @param {Roo.EventObject} e
5020          */
5021         "rowclick" : true,
5022         /**
5023          * @event rowdblclick
5024          * Fires when a row is double clicked
5025          * @param {Roo.bootstrap.Table} this
5026          * @param {Roo.Element} el
5027          * @param {Number} rowIndex
5028          * @param {Roo.EventObject} e
5029          */
5030         "rowdblclick" : true,
5031         /**
5032          * @event mouseover
5033          * Fires when a mouseover occur
5034          * @param {Roo.bootstrap.Table} this
5035          * @param {Roo.Element} el
5036          * @param {Number} rowIndex
5037          * @param {Number} columnIndex
5038          * @param {Roo.EventObject} e
5039          */
5040         "mouseover" : true,
5041         /**
5042          * @event mouseout
5043          * Fires when a mouseout 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         "mouseout" : true,
5051         /**
5052          * @event rowclass
5053          * Fires when a row is rendered, so you can change add a style to it.
5054          * @param {Roo.bootstrap.Table} this
5055          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5056          */
5057         'rowclass' : true
5058         
5059     });
5060 };
5061
5062 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5063     
5064     cls: false,
5065     align: false,
5066     bgcolor: false,
5067     border: false,
5068     cellpadding: false,
5069     cellspacing: false,
5070     frame: false,
5071     rules: false,
5072     sortable: false,
5073     summary: false,
5074     width: false,
5075     striped : false,
5076     bordered: false,
5077     hover:  false,
5078     condensed : false,
5079     responsive : false,
5080     sm : false,
5081     cm : false,
5082     store : false,
5083     loadMask : false,
5084     tfoot : true,
5085     thead : true,
5086     RowSelection : false,
5087     CellSelection : false,
5088     layout : false,
5089     
5090     // Roo.Element - the tbody
5091     mainBody: false, 
5092     
5093     getAutoCreate : function(){
5094         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5095         
5096         cfg = {
5097             tag: 'table',
5098             cls : 'table',
5099             cn : []
5100         }
5101             
5102         if (this.striped) {
5103             cfg.cls += ' table-striped';
5104         }
5105         
5106         if (this.hover) {
5107             cfg.cls += ' table-hover';
5108         }
5109         if (this.bordered) {
5110             cfg.cls += ' table-bordered';
5111         }
5112         if (this.condensed) {
5113             cfg.cls += ' table-condensed';
5114         }
5115         if (this.responsive) {
5116             cfg.cls += ' table-responsive';
5117         }
5118         
5119         if (this.cls) {
5120             cfg.cls+=  ' ' +this.cls;
5121         }
5122         
5123         // this lot should be simplifed...
5124         
5125         if (this.align) {
5126             cfg.align=this.align;
5127         }
5128         if (this.bgcolor) {
5129             cfg.bgcolor=this.bgcolor;
5130         }
5131         if (this.border) {
5132             cfg.border=this.border;
5133         }
5134         if (this.cellpadding) {
5135             cfg.cellpadding=this.cellpadding;
5136         }
5137         if (this.cellspacing) {
5138             cfg.cellspacing=this.cellspacing;
5139         }
5140         if (this.frame) {
5141             cfg.frame=this.frame;
5142         }
5143         if (this.rules) {
5144             cfg.rules=this.rules;
5145         }
5146         if (this.sortable) {
5147             cfg.sortable=this.sortable;
5148         }
5149         if (this.summary) {
5150             cfg.summary=this.summary;
5151         }
5152         if (this.width) {
5153             cfg.width=this.width;
5154         }
5155         if (this.layout) {
5156             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5157         }
5158         
5159         if(this.store || this.cm){
5160             if(this.thead){
5161                 cfg.cn.push(this.renderHeader());
5162             }
5163             
5164             cfg.cn.push(this.renderBody());
5165             
5166             if(this.tfoot){
5167                 cfg.cn.push(this.renderFooter());
5168             }
5169             
5170             cfg.cls+=  ' TableGrid';
5171         }
5172         
5173         return { cn : [ cfg ] };
5174     },
5175     
5176     initEvents : function()
5177     {   
5178         if(!this.store || !this.cm){
5179             return;
5180         }
5181         
5182         //Roo.log('initEvents with ds!!!!');
5183         
5184         this.mainBody = this.el.select('tbody', true).first();
5185         
5186         
5187         var _this = this;
5188         
5189         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5190             e.on('click', _this.sort, _this);
5191         });
5192         
5193         this.el.on("click", this.onClick, this);
5194         this.el.on("dblclick", this.onDblClick, this);
5195         
5196         // why is this done????? = it breaks dialogs??
5197         //this.parent().el.setStyle('position', 'relative');
5198         
5199         
5200         if (this.footer) {
5201             this.footer.parentId = this.id;
5202             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
5203         }
5204         
5205         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5206         
5207         this.store.on('load', this.onLoad, this);
5208         this.store.on('beforeload', this.onBeforeLoad, this);
5209         this.store.on('update', this.onUpdate, this);
5210         this.store.on('add', this.onAdd, this);
5211         
5212     },
5213     
5214     onMouseover : function(e, el)
5215     {
5216         var cell = Roo.get(el);
5217         
5218         if(!cell){
5219             return;
5220         }
5221         
5222         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5223             cell = cell.findParent('td', false, true);
5224         }
5225         
5226         var row = cell.findParent('tr', false, true);
5227         var cellIndex = cell.dom.cellIndex;
5228         var rowIndex = row.dom.rowIndex - 1; // start from 0
5229         
5230         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5231         
5232     },
5233     
5234     onMouseout : function(e, el)
5235     {
5236         var cell = Roo.get(el);
5237         
5238         if(!cell){
5239             return;
5240         }
5241         
5242         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5243             cell = cell.findParent('td', false, true);
5244         }
5245         
5246         var row = cell.findParent('tr', false, true);
5247         var cellIndex = cell.dom.cellIndex;
5248         var rowIndex = row.dom.rowIndex - 1; // start from 0
5249         
5250         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5251         
5252     },
5253     
5254     onClick : function(e, el)
5255     {
5256         var cell = Roo.get(el);
5257         
5258         if(!cell || (!this.CellSelection && !this.RowSelection)){
5259             return;
5260         }
5261         
5262         
5263         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5264             cell = cell.findParent('td', false, true);
5265         }
5266         
5267         var row = cell.findParent('tr', false, true);
5268         var cellIndex = cell.dom.cellIndex;
5269         var rowIndex = row.dom.rowIndex - 1;
5270         
5271         if(this.CellSelection){
5272             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5273         }
5274         
5275         if(this.RowSelection){
5276             this.fireEvent('rowclick', this, row, rowIndex, e);
5277         }
5278         
5279         
5280     },
5281     
5282     onDblClick : function(e,el)
5283     {
5284         var cell = Roo.get(el);
5285         
5286         if(!cell || (!this.CellSelection && !this.RowSelection)){
5287             return;
5288         }
5289         
5290         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5291             cell = cell.findParent('td', false, true);
5292         }
5293         
5294         var row = cell.findParent('tr', false, true);
5295         var cellIndex = cell.dom.cellIndex;
5296         var rowIndex = row.dom.rowIndex - 1;
5297         
5298         if(this.CellSelection){
5299             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5300         }
5301         
5302         if(this.RowSelection){
5303             this.fireEvent('rowdblclick', this, row, rowIndex, e);
5304         }
5305     },
5306     
5307     sort : function(e,el)
5308     {
5309         var col = Roo.get(el)
5310         
5311         if(!col.hasClass('sortable')){
5312             return;
5313         }
5314         
5315         var sort = col.attr('sort');
5316         var dir = 'ASC';
5317         
5318         if(col.hasClass('glyphicon-arrow-up')){
5319             dir = 'DESC';
5320         }
5321         
5322         this.store.sortInfo = {field : sort, direction : dir};
5323         
5324         if (this.footer) {
5325             Roo.log("calling footer first");
5326             this.footer.onClick('first');
5327         } else {
5328         
5329             this.store.load({ params : { start : 0 } });
5330         }
5331     },
5332     
5333     renderHeader : function()
5334     {
5335         var header = {
5336             tag: 'thead',
5337             cn : []
5338         };
5339         
5340         var cm = this.cm;
5341         
5342         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5343             
5344             var config = cm.config[i];
5345                     
5346             var c = {
5347                 tag: 'th',
5348                 style : '',
5349                 html: cm.getColumnHeader(i)
5350             };
5351             
5352             if(typeof(config.hidden) != 'undefined' && config.hidden){
5353                 c.style += ' display:none;';
5354             }
5355             
5356             if(typeof(config.dataIndex) != 'undefined'){
5357                 c.sort = config.dataIndex;
5358             }
5359             
5360             if(typeof(config.sortable) != 'undefined' && config.sortable){
5361                 c.cls = 'sortable';
5362             }
5363             
5364             if(typeof(config.align) != 'undefined' && config.align.length){
5365                 c.style += ' text-align:' + config.align + ';';
5366             }
5367             
5368             if(typeof(config.width) != 'undefined'){
5369                 c.style += ' width:' + config.width + 'px;';
5370             }
5371             
5372             header.cn.push(c)
5373         }
5374         
5375         return header;
5376     },
5377     
5378     renderBody : function()
5379     {
5380         var body = {
5381             tag: 'tbody',
5382             cn : [
5383                 {
5384                     tag: 'tr',
5385                     cn : [
5386                         {
5387                             tag : 'td',
5388                             colspan :  this.cm.getColumnCount()
5389                         }
5390                     ]
5391                 }
5392             ]
5393         };
5394         
5395         return body;
5396     },
5397     
5398     renderFooter : function()
5399     {
5400         var footer = {
5401             tag: 'tfoot',
5402             cn : [
5403                 {
5404                     tag: 'tr',
5405                     cn : [
5406                         {
5407                             tag : 'td',
5408                             colspan :  this.cm.getColumnCount()
5409                         }
5410                     ]
5411                 }
5412             ]
5413         };
5414         
5415         return footer;
5416     },
5417     
5418     
5419     
5420     onLoad : function()
5421     {
5422         Roo.log('ds onload');
5423         this.clear();
5424         
5425         var _this = this;
5426         var cm = this.cm;
5427         var ds = this.store;
5428         
5429         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5430             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
5431             
5432             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
5433                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
5434             }
5435             
5436             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
5437                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
5438             }
5439         });
5440         
5441         var tbody =  this.mainBody;
5442               
5443         if(ds.getCount() > 0){
5444             ds.data.each(function(d,rowIndex){
5445                 var row =  this.renderRow(cm, ds, rowIndex);
5446                 
5447                 tbody.createChild(row);
5448                 
5449                 var _this = this;
5450                 
5451                 if(row.cellObjects.length){
5452                     Roo.each(row.cellObjects, function(r){
5453                         _this.renderCellObject(r);
5454                     })
5455                 }
5456                 
5457             }, this);
5458         }
5459         
5460         Roo.each(this.el.select('tbody td', true).elements, function(e){
5461             e.on('mouseover', _this.onMouseover, _this);
5462         });
5463         
5464         Roo.each(this.el.select('tbody td', true).elements, function(e){
5465             e.on('mouseout', _this.onMouseout, _this);
5466         });
5467
5468         //if(this.loadMask){
5469         //    this.maskEl.hide();
5470         //}
5471     },
5472     
5473     
5474     onUpdate : function(ds,record)
5475     {
5476         this.refreshRow(record);
5477     },
5478     onRemove : function(ds, record, index, isUpdate){
5479         if(isUpdate !== true){
5480             this.fireEvent("beforerowremoved", this, index, record);
5481         }
5482         var bt = this.mainBody.dom;
5483         if(bt.rows[index]){
5484             bt.removeChild(bt.rows[index]);
5485         }
5486         
5487         if(isUpdate !== true){
5488             //this.stripeRows(index);
5489             //this.syncRowHeights(index, index);
5490             //this.layout();
5491             this.fireEvent("rowremoved", this, index, record);
5492         }
5493     },
5494     
5495     onAdd : function(ds, records, rowIndex)
5496     {
5497         //Roo.log('on Add called');
5498         // - note this does not handle multiple adding very well..
5499         var bt = this.mainBody.dom;
5500         for (var i =0 ; i < records.length;i++) {
5501             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
5502             //Roo.log(records[i]);
5503             //Roo.log(this.store.getAt(rowIndex+i));
5504             this.insertRow(this.store, rowIndex + i, false);
5505             return;
5506         }
5507         
5508     },
5509     
5510     
5511     refreshRow : function(record){
5512         var ds = this.store, index;
5513         if(typeof record == 'number'){
5514             index = record;
5515             record = ds.getAt(index);
5516         }else{
5517             index = ds.indexOf(record);
5518         }
5519         this.insertRow(ds, index, true);
5520         this.onRemove(ds, record, index+1, true);
5521         //this.syncRowHeights(index, index);
5522         //this.layout();
5523         this.fireEvent("rowupdated", this, index, record);
5524     },
5525     
5526     insertRow : function(dm, rowIndex, isUpdate){
5527         
5528         if(!isUpdate){
5529             this.fireEvent("beforerowsinserted", this, rowIndex);
5530         }
5531             //var s = this.getScrollState();
5532         var row = this.renderRow(this.cm, this.store, rowIndex);
5533         // insert before rowIndex..
5534         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
5535         
5536         var _this = this;
5537                 
5538         if(row.cellObjects.length){
5539             Roo.each(row.cellObjects, function(r){
5540                 _this.renderCellObject(r);
5541             })
5542         }
5543             
5544         if(!isUpdate){
5545             this.fireEvent("rowsinserted", this, rowIndex);
5546             //this.syncRowHeights(firstRow, lastRow);
5547             //this.stripeRows(firstRow);
5548             //this.layout();
5549         }
5550         
5551     },
5552     
5553     
5554     getRowDom : function(rowIndex)
5555     {
5556         // not sure if I need to check this.. but let's do it anyway..
5557         return (this.mainBody.dom.rows && (rowIndex-1) < this.mainBody.dom.rows.length ) ?
5558                 this.mainBody.dom.rows[rowIndex] : false
5559     },
5560     // returns the object tree for a tr..
5561   
5562     
5563     renderRow : function(cm, ds, rowIndex) {
5564         
5565         var d = ds.getAt(rowIndex);
5566         
5567         var row = {
5568             tag : 'tr',
5569             cn : []
5570         };
5571             
5572         var cellObjects = [];
5573         
5574         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5575             var config = cm.config[i];
5576             
5577             var renderer = cm.getRenderer(i);
5578             var value = '';
5579             var id = false;
5580             
5581             if(typeof(renderer) !== 'undefined'){
5582                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
5583             }
5584             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
5585             // and are rendered into the cells after the row is rendered - using the id for the element.
5586             
5587             if(typeof(value) === 'object'){
5588                 id = Roo.id();
5589                 cellObjects.push({
5590                     container : id,
5591                     cfg : value 
5592                 })
5593             }
5594             
5595             var rowcfg = {
5596                 record: d,
5597                 rowIndex : rowIndex,
5598                 colIndex : i,
5599                 rowClass : ''
5600             }
5601
5602             this.fireEvent('rowclass', this, rowcfg);
5603             
5604             var td = {
5605                 tag: 'td',
5606                 cls : rowcfg.rowClass,
5607                 style: '',
5608                 html: (typeof(value) === 'object') ? '' : value
5609             };
5610             
5611             if (id) {
5612                 td.id = id;
5613             }
5614             
5615             if(typeof(config.hidden) != 'undefined' && config.hidden){
5616                 td.style += ' display:none;';
5617             }
5618             
5619             if(typeof(config.align) != 'undefined' && config.align.length){
5620                 td.style += ' text-align:' + config.align + ';';
5621             }
5622             
5623             if(typeof(config.width) != 'undefined'){
5624                 td.style += ' width:' +  config.width + 'px;';
5625             }
5626             
5627             if(typeof(config.cursor) != 'undefined'){
5628                 td.style += ' cursor:' +  config.cursor + ';';
5629             }
5630              
5631             row.cn.push(td);
5632            
5633         }
5634         
5635         row.cellObjects = cellObjects;
5636         
5637         return row;
5638           
5639     },
5640     
5641     
5642     
5643     onBeforeLoad : function()
5644     {
5645         //Roo.log('ds onBeforeLoad');
5646         
5647         //this.clear();
5648         
5649         //if(this.loadMask){
5650         //    this.maskEl.show();
5651         //}
5652     },
5653      /**
5654      * Remove all rows
5655      */
5656     clear : function()
5657     {
5658         this.el.select('tbody', true).first().dom.innerHTML = '';
5659     },
5660     /**
5661      * Show or hide a row.
5662      * @param {Number} rowIndex to show or hide
5663      * @param {Boolean} state hide
5664      */
5665     setRowVisibility : function(rowIndex, state)
5666     {
5667         var bt = this.mainBody.dom;
5668         if(typeof(bt.rows[rowIndex]) == 'undefined'){
5669             return;
5670         }
5671         bt.rows[rowIndex].style.display = state ? '' : 'none';
5672     },
5673     
5674     
5675     getSelectionModel : function(){
5676         if(!this.selModel){
5677             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
5678         }
5679         return this.selModel;
5680     },
5681     /*
5682      * Render the Roo.bootstrap object from renderder
5683      */
5684     renderCellObject : function(r)
5685     {
5686         var _this = this;
5687         
5688         var t = r.cfg.render(r.container);
5689         
5690         if(r.cfg.cn){
5691             Roo.each(r.cfg.cn, function(c){
5692                 var child = {
5693                     container: t.getChildContainer(),
5694                     cfg: c
5695                 }
5696                 _this.renderCellObject(child);
5697             })
5698         }
5699     }
5700    
5701 });
5702
5703  
5704
5705  /*
5706  * - LGPL
5707  *
5708  * table cell
5709  * 
5710  */
5711
5712 /**
5713  * @class Roo.bootstrap.TableCell
5714  * @extends Roo.bootstrap.Component
5715  * Bootstrap TableCell class
5716  * @cfg {String} html cell contain text
5717  * @cfg {String} cls cell class
5718  * @cfg {String} tag cell tag (td|th) default td
5719  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
5720  * @cfg {String} align Aligns the content in a cell
5721  * @cfg {String} axis Categorizes cells
5722  * @cfg {String} bgcolor Specifies the background color of a cell
5723  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5724  * @cfg {Number} colspan Specifies the number of columns a cell should span
5725  * @cfg {String} headers Specifies one or more header cells a cell is related to
5726  * @cfg {Number} height Sets the height of a cell
5727  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
5728  * @cfg {Number} rowspan Sets the number of rows a cell should span
5729  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
5730  * @cfg {String} valign Vertical aligns the content in a cell
5731  * @cfg {Number} width Specifies the width of a cell
5732  * 
5733  * @constructor
5734  * Create a new TableCell
5735  * @param {Object} config The config object
5736  */
5737
5738 Roo.bootstrap.TableCell = function(config){
5739     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
5740 };
5741
5742 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
5743     
5744     html: false,
5745     cls: false,
5746     tag: false,
5747     abbr: false,
5748     align: false,
5749     axis: false,
5750     bgcolor: false,
5751     charoff: false,
5752     colspan: false,
5753     headers: false,
5754     height: false,
5755     nowrap: false,
5756     rowspan: false,
5757     scope: false,
5758     valign: false,
5759     width: false,
5760     
5761     
5762     getAutoCreate : function(){
5763         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
5764         
5765         cfg = {
5766             tag: 'td'
5767         }
5768         
5769         if(this.tag){
5770             cfg.tag = this.tag;
5771         }
5772         
5773         if (this.html) {
5774             cfg.html=this.html
5775         }
5776         if (this.cls) {
5777             cfg.cls=this.cls
5778         }
5779         if (this.abbr) {
5780             cfg.abbr=this.abbr
5781         }
5782         if (this.align) {
5783             cfg.align=this.align
5784         }
5785         if (this.axis) {
5786             cfg.axis=this.axis
5787         }
5788         if (this.bgcolor) {
5789             cfg.bgcolor=this.bgcolor
5790         }
5791         if (this.charoff) {
5792             cfg.charoff=this.charoff
5793         }
5794         if (this.colspan) {
5795             cfg.colspan=this.colspan
5796         }
5797         if (this.headers) {
5798             cfg.headers=this.headers
5799         }
5800         if (this.height) {
5801             cfg.height=this.height
5802         }
5803         if (this.nowrap) {
5804             cfg.nowrap=this.nowrap
5805         }
5806         if (this.rowspan) {
5807             cfg.rowspan=this.rowspan
5808         }
5809         if (this.scope) {
5810             cfg.scope=this.scope
5811         }
5812         if (this.valign) {
5813             cfg.valign=this.valign
5814         }
5815         if (this.width) {
5816             cfg.width=this.width
5817         }
5818         
5819         
5820         return cfg;
5821     }
5822    
5823 });
5824
5825  
5826
5827  /*
5828  * - LGPL
5829  *
5830  * table row
5831  * 
5832  */
5833
5834 /**
5835  * @class Roo.bootstrap.TableRow
5836  * @extends Roo.bootstrap.Component
5837  * Bootstrap TableRow class
5838  * @cfg {String} cls row class
5839  * @cfg {String} align Aligns the content in a table row
5840  * @cfg {String} bgcolor Specifies a background color for a table row
5841  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5842  * @cfg {String} valign Vertical aligns the content in a table row
5843  * 
5844  * @constructor
5845  * Create a new TableRow
5846  * @param {Object} config The config object
5847  */
5848
5849 Roo.bootstrap.TableRow = function(config){
5850     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
5851 };
5852
5853 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
5854     
5855     cls: false,
5856     align: false,
5857     bgcolor: false,
5858     charoff: false,
5859     valign: false,
5860     
5861     getAutoCreate : function(){
5862         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
5863         
5864         cfg = {
5865             tag: 'tr'
5866         }
5867             
5868         if(this.cls){
5869             cfg.cls = this.cls;
5870         }
5871         if(this.align){
5872             cfg.align = this.align;
5873         }
5874         if(this.bgcolor){
5875             cfg.bgcolor = this.bgcolor;
5876         }
5877         if(this.charoff){
5878             cfg.charoff = this.charoff;
5879         }
5880         if(this.valign){
5881             cfg.valign = this.valign;
5882         }
5883         
5884         return cfg;
5885     }
5886    
5887 });
5888
5889  
5890
5891  /*
5892  * - LGPL
5893  *
5894  * table body
5895  * 
5896  */
5897
5898 /**
5899  * @class Roo.bootstrap.TableBody
5900  * @extends Roo.bootstrap.Component
5901  * Bootstrap TableBody class
5902  * @cfg {String} cls element class
5903  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
5904  * @cfg {String} align Aligns the content inside the element
5905  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
5906  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
5907  * 
5908  * @constructor
5909  * Create a new TableBody
5910  * @param {Object} config The config object
5911  */
5912
5913 Roo.bootstrap.TableBody = function(config){
5914     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
5915 };
5916
5917 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
5918     
5919     cls: false,
5920     tag: false,
5921     align: false,
5922     charoff: false,
5923     valign: false,
5924     
5925     getAutoCreate : function(){
5926         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
5927         
5928         cfg = {
5929             tag: 'tbody'
5930         }
5931             
5932         if (this.cls) {
5933             cfg.cls=this.cls
5934         }
5935         if(this.tag){
5936             cfg.tag = this.tag;
5937         }
5938         
5939         if(this.align){
5940             cfg.align = this.align;
5941         }
5942         if(this.charoff){
5943             cfg.charoff = this.charoff;
5944         }
5945         if(this.valign){
5946             cfg.valign = this.valign;
5947         }
5948         
5949         return cfg;
5950     }
5951     
5952     
5953 //    initEvents : function()
5954 //    {
5955 //        
5956 //        if(!this.store){
5957 //            return;
5958 //        }
5959 //        
5960 //        this.store = Roo.factory(this.store, Roo.data);
5961 //        this.store.on('load', this.onLoad, this);
5962 //        
5963 //        this.store.load();
5964 //        
5965 //    },
5966 //    
5967 //    onLoad: function () 
5968 //    {   
5969 //        this.fireEvent('load', this);
5970 //    }
5971 //    
5972 //   
5973 });
5974
5975  
5976
5977  /*
5978  * Based on:
5979  * Ext JS Library 1.1.1
5980  * Copyright(c) 2006-2007, Ext JS, LLC.
5981  *
5982  * Originally Released Under LGPL - original licence link has changed is not relivant.
5983  *
5984  * Fork - LGPL
5985  * <script type="text/javascript">
5986  */
5987
5988 // as we use this in bootstrap.
5989 Roo.namespace('Roo.form');
5990  /**
5991  * @class Roo.form.Action
5992  * Internal Class used to handle form actions
5993  * @constructor
5994  * @param {Roo.form.BasicForm} el The form element or its id
5995  * @param {Object} config Configuration options
5996  */
5997
5998  
5999  
6000 // define the action interface
6001 Roo.form.Action = function(form, options){
6002     this.form = form;
6003     this.options = options || {};
6004 };
6005 /**
6006  * Client Validation Failed
6007  * @const 
6008  */
6009 Roo.form.Action.CLIENT_INVALID = 'client';
6010 /**
6011  * Server Validation Failed
6012  * @const 
6013  */
6014 Roo.form.Action.SERVER_INVALID = 'server';
6015  /**
6016  * Connect to Server Failed
6017  * @const 
6018  */
6019 Roo.form.Action.CONNECT_FAILURE = 'connect';
6020 /**
6021  * Reading Data from Server Failed
6022  * @const 
6023  */
6024 Roo.form.Action.LOAD_FAILURE = 'load';
6025
6026 Roo.form.Action.prototype = {
6027     type : 'default',
6028     failureType : undefined,
6029     response : undefined,
6030     result : undefined,
6031
6032     // interface method
6033     run : function(options){
6034
6035     },
6036
6037     // interface method
6038     success : function(response){
6039
6040     },
6041
6042     // interface method
6043     handleResponse : function(response){
6044
6045     },
6046
6047     // default connection failure
6048     failure : function(response){
6049         
6050         this.response = response;
6051         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6052         this.form.afterAction(this, false);
6053     },
6054
6055     processResponse : function(response){
6056         this.response = response;
6057         if(!response.responseText){
6058             return true;
6059         }
6060         this.result = this.handleResponse(response);
6061         return this.result;
6062     },
6063
6064     // utility functions used internally
6065     getUrl : function(appendParams){
6066         var url = this.options.url || this.form.url || this.form.el.dom.action;
6067         if(appendParams){
6068             var p = this.getParams();
6069             if(p){
6070                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
6071             }
6072         }
6073         return url;
6074     },
6075
6076     getMethod : function(){
6077         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
6078     },
6079
6080     getParams : function(){
6081         var bp = this.form.baseParams;
6082         var p = this.options.params;
6083         if(p){
6084             if(typeof p == "object"){
6085                 p = Roo.urlEncode(Roo.applyIf(p, bp));
6086             }else if(typeof p == 'string' && bp){
6087                 p += '&' + Roo.urlEncode(bp);
6088             }
6089         }else if(bp){
6090             p = Roo.urlEncode(bp);
6091         }
6092         return p;
6093     },
6094
6095     createCallback : function(){
6096         return {
6097             success: this.success,
6098             failure: this.failure,
6099             scope: this,
6100             timeout: (this.form.timeout*1000),
6101             upload: this.form.fileUpload ? this.success : undefined
6102         };
6103     }
6104 };
6105
6106 Roo.form.Action.Submit = function(form, options){
6107     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
6108 };
6109
6110 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
6111     type : 'submit',
6112
6113     haveProgress : false,
6114     uploadComplete : false,
6115     
6116     // uploadProgress indicator.
6117     uploadProgress : function()
6118     {
6119         if (!this.form.progressUrl) {
6120             return;
6121         }
6122         
6123         if (!this.haveProgress) {
6124             Roo.MessageBox.progress("Uploading", "Uploading");
6125         }
6126         if (this.uploadComplete) {
6127            Roo.MessageBox.hide();
6128            return;
6129         }
6130         
6131         this.haveProgress = true;
6132    
6133         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6134         
6135         var c = new Roo.data.Connection();
6136         c.request({
6137             url : this.form.progressUrl,
6138             params: {
6139                 id : uid
6140             },
6141             method: 'GET',
6142             success : function(req){
6143                //console.log(data);
6144                 var rdata = false;
6145                 var edata;
6146                 try  {
6147                    rdata = Roo.decode(req.responseText)
6148                 } catch (e) {
6149                     Roo.log("Invalid data from server..");
6150                     Roo.log(edata);
6151                     return;
6152                 }
6153                 if (!rdata || !rdata.success) {
6154                     Roo.log(rdata);
6155                     Roo.MessageBox.alert(Roo.encode(rdata));
6156                     return;
6157                 }
6158                 var data = rdata.data;
6159                 
6160                 if (this.uploadComplete) {
6161                    Roo.MessageBox.hide();
6162                    return;
6163                 }
6164                    
6165                 if (data){
6166                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6167                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6168                     );
6169                 }
6170                 this.uploadProgress.defer(2000,this);
6171             },
6172        
6173             failure: function(data) {
6174                 Roo.log('progress url failed ');
6175                 Roo.log(data);
6176             },
6177             scope : this
6178         });
6179            
6180     },
6181     
6182     
6183     run : function()
6184     {
6185         // run get Values on the form, so it syncs any secondary forms.
6186         this.form.getValues();
6187         
6188         var o = this.options;
6189         var method = this.getMethod();
6190         var isPost = method == 'POST';
6191         if(o.clientValidation === false || this.form.isValid()){
6192             
6193             if (this.form.progressUrl) {
6194                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6195                     (new Date() * 1) + '' + Math.random());
6196                     
6197             } 
6198             
6199             
6200             Roo.Ajax.request(Roo.apply(this.createCallback(), {
6201                 form:this.form.el.dom,
6202                 url:this.getUrl(!isPost),
6203                 method: method,
6204                 params:isPost ? this.getParams() : null,
6205                 isUpload: this.form.fileUpload
6206             }));
6207             
6208             this.uploadProgress();
6209
6210         }else if (o.clientValidation !== false){ // client validation failed
6211             this.failureType = Roo.form.Action.CLIENT_INVALID;
6212             this.form.afterAction(this, false);
6213         }
6214     },
6215
6216     success : function(response)
6217     {
6218         this.uploadComplete= true;
6219         if (this.haveProgress) {
6220             Roo.MessageBox.hide();
6221         }
6222         
6223         
6224         var result = this.processResponse(response);
6225         if(result === true || result.success){
6226             this.form.afterAction(this, true);
6227             return;
6228         }
6229         if(result.errors){
6230             this.form.markInvalid(result.errors);
6231             this.failureType = Roo.form.Action.SERVER_INVALID;
6232         }
6233         this.form.afterAction(this, false);
6234     },
6235     failure : function(response)
6236     {
6237         this.uploadComplete= true;
6238         if (this.haveProgress) {
6239             Roo.MessageBox.hide();
6240         }
6241         
6242         this.response = response;
6243         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6244         this.form.afterAction(this, false);
6245     },
6246     
6247     handleResponse : function(response){
6248         if(this.form.errorReader){
6249             var rs = this.form.errorReader.read(response);
6250             var errors = [];
6251             if(rs.records){
6252                 for(var i = 0, len = rs.records.length; i < len; i++) {
6253                     var r = rs.records[i];
6254                     errors[i] = r.data;
6255                 }
6256             }
6257             if(errors.length < 1){
6258                 errors = null;
6259             }
6260             return {
6261                 success : rs.success,
6262                 errors : errors
6263             };
6264         }
6265         var ret = false;
6266         try {
6267             ret = Roo.decode(response.responseText);
6268         } catch (e) {
6269             ret = {
6270                 success: false,
6271                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6272                 errors : []
6273             };
6274         }
6275         return ret;
6276         
6277     }
6278 });
6279
6280
6281 Roo.form.Action.Load = function(form, options){
6282     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6283     this.reader = this.form.reader;
6284 };
6285
6286 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6287     type : 'load',
6288
6289     run : function(){
6290         
6291         Roo.Ajax.request(Roo.apply(
6292                 this.createCallback(), {
6293                     method:this.getMethod(),
6294                     url:this.getUrl(false),
6295                     params:this.getParams()
6296         }));
6297     },
6298
6299     success : function(response){
6300         
6301         var result = this.processResponse(response);
6302         if(result === true || !result.success || !result.data){
6303             this.failureType = Roo.form.Action.LOAD_FAILURE;
6304             this.form.afterAction(this, false);
6305             return;
6306         }
6307         this.form.clearInvalid();
6308         this.form.setValues(result.data);
6309         this.form.afterAction(this, true);
6310     },
6311
6312     handleResponse : function(response){
6313         if(this.form.reader){
6314             var rs = this.form.reader.read(response);
6315             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
6316             return {
6317                 success : rs.success,
6318                 data : data
6319             };
6320         }
6321         return Roo.decode(response.responseText);
6322     }
6323 });
6324
6325 Roo.form.Action.ACTION_TYPES = {
6326     'load' : Roo.form.Action.Load,
6327     'submit' : Roo.form.Action.Submit
6328 };/*
6329  * - LGPL
6330  *
6331  * form
6332  * 
6333  */
6334
6335 /**
6336  * @class Roo.bootstrap.Form
6337  * @extends Roo.bootstrap.Component
6338  * Bootstrap Form class
6339  * @cfg {String} method  GET | POST (default POST)
6340  * @cfg {String} labelAlign top | left (default top)
6341  * @cfg {String} align left  | right - for navbars
6342  * @cfg {Boolean} loadMask load mask when submit (default true)
6343
6344  * 
6345  * @constructor
6346  * Create a new Form
6347  * @param {Object} config The config object
6348  */
6349
6350
6351 Roo.bootstrap.Form = function(config){
6352     Roo.bootstrap.Form.superclass.constructor.call(this, config);
6353     this.addEvents({
6354         /**
6355          * @event clientvalidation
6356          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
6357          * @param {Form} this
6358          * @param {Boolean} valid true if the form has passed client-side validation
6359          */
6360         clientvalidation: true,
6361         /**
6362          * @event beforeaction
6363          * Fires before any action is performed. Return false to cancel the action.
6364          * @param {Form} this
6365          * @param {Action} action The action to be performed
6366          */
6367         beforeaction: true,
6368         /**
6369          * @event actionfailed
6370          * Fires when an action fails.
6371          * @param {Form} this
6372          * @param {Action} action The action that failed
6373          */
6374         actionfailed : true,
6375         /**
6376          * @event actioncomplete
6377          * Fires when an action is completed.
6378          * @param {Form} this
6379          * @param {Action} action The action that completed
6380          */
6381         actioncomplete : true
6382     });
6383     
6384 };
6385
6386 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
6387       
6388      /**
6389      * @cfg {String} method
6390      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
6391      */
6392     method : 'POST',
6393     /**
6394      * @cfg {String} url
6395      * The URL to use for form actions if one isn't supplied in the action options.
6396      */
6397     /**
6398      * @cfg {Boolean} fileUpload
6399      * Set to true if this form is a file upload.
6400      */
6401      
6402     /**
6403      * @cfg {Object} baseParams
6404      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
6405      */
6406       
6407     /**
6408      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
6409      */
6410     timeout: 30,
6411     /**
6412      * @cfg {Sting} align (left|right) for navbar forms
6413      */
6414     align : 'left',
6415
6416     // private
6417     activeAction : null,
6418  
6419     /**
6420      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6421      * element by passing it or its id or mask the form itself by passing in true.
6422      * @type Mixed
6423      */
6424     waitMsgTarget : false,
6425     
6426     loadMask : true,
6427     
6428     getAutoCreate : function(){
6429         
6430         var cfg = {
6431             tag: 'form',
6432             method : this.method || 'POST',
6433             id : this.id || Roo.id(),
6434             cls : ''
6435         }
6436         if (this.parent().xtype.match(/^Nav/)) {
6437             cfg.cls = 'navbar-form navbar-' + this.align;
6438             
6439         }
6440         
6441         if (this.labelAlign == 'left' ) {
6442             cfg.cls += ' form-horizontal';
6443         }
6444         
6445         
6446         return cfg;
6447     },
6448     initEvents : function()
6449     {
6450         this.el.on('submit', this.onSubmit, this);
6451         // this was added as random key presses on the form where triggering form submit.
6452         this.el.on('keypress', function(e) {
6453             if (e.getCharCode() != 13) {
6454                 return true;
6455             }
6456             // we might need to allow it for textareas.. and some other items.
6457             // check e.getTarget().
6458             
6459             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
6460                 return true;
6461             }
6462         
6463             Roo.log("keypress blocked");
6464             
6465             e.preventDefault();
6466             return false;
6467         });
6468         
6469     },
6470     // private
6471     onSubmit : function(e){
6472         e.stopEvent();
6473     },
6474     
6475      /**
6476      * Returns true if client-side validation on the form is successful.
6477      * @return Boolean
6478      */
6479     isValid : function(){
6480         var items = this.getItems();
6481         var valid = true;
6482         items.each(function(f){
6483            if(!f.validate()){
6484                valid = false;
6485                
6486            }
6487         });
6488         return valid;
6489     },
6490     /**
6491      * Returns true if any fields in this form have changed since their original load.
6492      * @return Boolean
6493      */
6494     isDirty : function(){
6495         var dirty = false;
6496         var items = this.getItems();
6497         items.each(function(f){
6498            if(f.isDirty()){
6499                dirty = true;
6500                return false;
6501            }
6502            return true;
6503         });
6504         return dirty;
6505     },
6506      /**
6507      * Performs a predefined action (submit or load) or custom actions you define on this form.
6508      * @param {String} actionName The name of the action type
6509      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
6510      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
6511      * accept other config options):
6512      * <pre>
6513 Property          Type             Description
6514 ----------------  ---------------  ----------------------------------------------------------------------------------
6515 url               String           The url for the action (defaults to the form's url)
6516 method            String           The form method to use (defaults to the form's method, or POST if not defined)
6517 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
6518 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
6519                                    validate the form on the client (defaults to false)
6520      * </pre>
6521      * @return {BasicForm} this
6522      */
6523     doAction : function(action, options){
6524         if(typeof action == 'string'){
6525             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
6526         }
6527         if(this.fireEvent('beforeaction', this, action) !== false){
6528             this.beforeAction(action);
6529             action.run.defer(100, action);
6530         }
6531         return this;
6532     },
6533     
6534     // private
6535     beforeAction : function(action){
6536         var o = action.options;
6537         
6538         if(this.loadMask){
6539             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6540         }
6541         // not really supported yet.. ??
6542         
6543         //if(this.waitMsgTarget === true){
6544         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6545         //}else if(this.waitMsgTarget){
6546         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
6547         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
6548         //}else {
6549         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
6550        // }
6551          
6552     },
6553
6554     // private
6555     afterAction : function(action, success){
6556         this.activeAction = null;
6557         var o = action.options;
6558         
6559         //if(this.waitMsgTarget === true){
6560             this.el.unmask();
6561         //}else if(this.waitMsgTarget){
6562         //    this.waitMsgTarget.unmask();
6563         //}else{
6564         //    Roo.MessageBox.updateProgress(1);
6565         //    Roo.MessageBox.hide();
6566        // }
6567         // 
6568         if(success){
6569             if(o.reset){
6570                 this.reset();
6571             }
6572             Roo.callback(o.success, o.scope, [this, action]);
6573             this.fireEvent('actioncomplete', this, action);
6574             
6575         }else{
6576             
6577             // failure condition..
6578             // we have a scenario where updates need confirming.
6579             // eg. if a locking scenario exists..
6580             // we look for { errors : { needs_confirm : true }} in the response.
6581             if (
6582                 (typeof(action.result) != 'undefined')  &&
6583                 (typeof(action.result.errors) != 'undefined')  &&
6584                 (typeof(action.result.errors.needs_confirm) != 'undefined')
6585            ){
6586                 var _t = this;
6587                 Roo.log("not supported yet");
6588                  /*
6589                 
6590                 Roo.MessageBox.confirm(
6591                     "Change requires confirmation",
6592                     action.result.errorMsg,
6593                     function(r) {
6594                         if (r != 'yes') {
6595                             return;
6596                         }
6597                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
6598                     }
6599                     
6600                 );
6601                 */
6602                 
6603                 
6604                 return;
6605             }
6606             
6607             Roo.callback(o.failure, o.scope, [this, action]);
6608             // show an error message if no failed handler is set..
6609             if (!this.hasListener('actionfailed')) {
6610                 Roo.log("need to add dialog support");
6611                 /*
6612                 Roo.MessageBox.alert("Error",
6613                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
6614                         action.result.errorMsg :
6615                         "Saving Failed, please check your entries or try again"
6616                 );
6617                 */
6618             }
6619             
6620             this.fireEvent('actionfailed', this, action);
6621         }
6622         
6623     },
6624     /**
6625      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
6626      * @param {String} id The value to search for
6627      * @return Field
6628      */
6629     findField : function(id){
6630         var items = this.getItems();
6631         var field = items.get(id);
6632         if(!field){
6633              items.each(function(f){
6634                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
6635                     field = f;
6636                     return false;
6637                 }
6638                 return true;
6639             });
6640         }
6641         return field || null;
6642     },
6643      /**
6644      * Mark fields in this form invalid in bulk.
6645      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
6646      * @return {BasicForm} this
6647      */
6648     markInvalid : function(errors){
6649         if(errors instanceof Array){
6650             for(var i = 0, len = errors.length; i < len; i++){
6651                 var fieldError = errors[i];
6652                 var f = this.findField(fieldError.id);
6653                 if(f){
6654                     f.markInvalid(fieldError.msg);
6655                 }
6656             }
6657         }else{
6658             var field, id;
6659             for(id in errors){
6660                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
6661                     field.markInvalid(errors[id]);
6662                 }
6663             }
6664         }
6665         //Roo.each(this.childForms || [], function (f) {
6666         //    f.markInvalid(errors);
6667         //});
6668         
6669         return this;
6670     },
6671
6672     /**
6673      * Set values for fields in this form in bulk.
6674      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
6675      * @return {BasicForm} this
6676      */
6677     setValues : function(values){
6678         if(values instanceof Array){ // array of objects
6679             for(var i = 0, len = values.length; i < len; i++){
6680                 var v = values[i];
6681                 var f = this.findField(v.id);
6682                 if(f){
6683                     f.setValue(v.value);
6684                     if(this.trackResetOnLoad){
6685                         f.originalValue = f.getValue();
6686                     }
6687                 }
6688             }
6689         }else{ // object hash
6690             var field, id;
6691             for(id in values){
6692                 if(typeof values[id] != 'function' && (field = this.findField(id))){
6693                     
6694                     if (field.setFromData && 
6695                         field.valueField && 
6696                         field.displayField &&
6697                         // combos' with local stores can 
6698                         // be queried via setValue()
6699                         // to set their value..
6700                         (field.store && !field.store.isLocal)
6701                         ) {
6702                         // it's a combo
6703                         var sd = { };
6704                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
6705                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
6706                         field.setFromData(sd);
6707                         
6708                     } else {
6709                         field.setValue(values[id]);
6710                     }
6711                     
6712                     
6713                     if(this.trackResetOnLoad){
6714                         field.originalValue = field.getValue();
6715                     }
6716                 }
6717             }
6718         }
6719          
6720         //Roo.each(this.childForms || [], function (f) {
6721         //    f.setValues(values);
6722         //});
6723                 
6724         return this;
6725     },
6726
6727     /**
6728      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
6729      * they are returned as an array.
6730      * @param {Boolean} asString
6731      * @return {Object}
6732      */
6733     getValues : function(asString){
6734         //if (this.childForms) {
6735             // copy values from the child forms
6736         //    Roo.each(this.childForms, function (f) {
6737         //        this.setValues(f.getValues());
6738         //    }, this);
6739         //}
6740         
6741         
6742         
6743         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
6744         if(asString === true){
6745             return fs;
6746         }
6747         return Roo.urlDecode(fs);
6748     },
6749     
6750     /**
6751      * Returns the fields in this form as an object with key/value pairs. 
6752      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
6753      * @return {Object}
6754      */
6755     getFieldValues : function(with_hidden)
6756     {
6757         var items = this.getItems();
6758         var ret = {};
6759         items.each(function(f){
6760             if (!f.getName()) {
6761                 return;
6762             }
6763             var v = f.getValue();
6764             if (f.inputType =='radio') {
6765                 if (typeof(ret[f.getName()]) == 'undefined') {
6766                     ret[f.getName()] = ''; // empty..
6767                 }
6768                 
6769                 if (!f.el.dom.checked) {
6770                     return;
6771                     
6772                 }
6773                 v = f.el.dom.value;
6774                 
6775             }
6776             
6777             // not sure if this supported any more..
6778             if ((typeof(v) == 'object') && f.getRawValue) {
6779                 v = f.getRawValue() ; // dates..
6780             }
6781             // combo boxes where name != hiddenName...
6782             if (f.name != f.getName()) {
6783                 ret[f.name] = f.getRawValue();
6784             }
6785             ret[f.getName()] = v;
6786         });
6787         
6788         return ret;
6789     },
6790
6791     /**
6792      * Clears all invalid messages in this form.
6793      * @return {BasicForm} this
6794      */
6795     clearInvalid : function(){
6796         var items = this.getItems();
6797         
6798         items.each(function(f){
6799            f.clearInvalid();
6800         });
6801         
6802         
6803         
6804         return this;
6805     },
6806
6807     /**
6808      * Resets this form.
6809      * @return {BasicForm} this
6810      */
6811     reset : function(){
6812         var items = this.getItems();
6813         items.each(function(f){
6814             f.reset();
6815         });
6816         
6817         Roo.each(this.childForms || [], function (f) {
6818             f.reset();
6819         });
6820        
6821         
6822         return this;
6823     },
6824     getItems : function()
6825     {
6826         var r=new Roo.util.MixedCollection(false, function(o){
6827             return o.id || (o.id = Roo.id());
6828         });
6829         var iter = function(el) {
6830             if (el.inputEl) {
6831                 r.add(el);
6832             }
6833             if (!el.items) {
6834                 return;
6835             }
6836             Roo.each(el.items,function(e) {
6837                 iter(e);
6838             });
6839             
6840             
6841         };
6842         iter(this);
6843         return r;
6844         
6845         
6846         
6847         
6848     }
6849     
6850 });
6851
6852  
6853 /*
6854  * Based on:
6855  * Ext JS Library 1.1.1
6856  * Copyright(c) 2006-2007, Ext JS, LLC.
6857  *
6858  * Originally Released Under LGPL - original licence link has changed is not relivant.
6859  *
6860  * Fork - LGPL
6861  * <script type="text/javascript">
6862  */
6863 /**
6864  * @class Roo.form.VTypes
6865  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
6866  * @singleton
6867  */
6868 Roo.form.VTypes = function(){
6869     // closure these in so they are only created once.
6870     var alpha = /^[a-zA-Z_]+$/;
6871     var alphanum = /^[a-zA-Z0-9_]+$/;
6872     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
6873     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
6874
6875     // All these messages and functions are configurable
6876     return {
6877         /**
6878          * The function used to validate email addresses
6879          * @param {String} value The email address
6880          */
6881         'email' : function(v){
6882             return email.test(v);
6883         },
6884         /**
6885          * The error text to display when the email validation function returns false
6886          * @type String
6887          */
6888         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
6889         /**
6890          * The keystroke filter mask to be applied on email input
6891          * @type RegExp
6892          */
6893         'emailMask' : /[a-z0-9_\.\-@]/i,
6894
6895         /**
6896          * The function used to validate URLs
6897          * @param {String} value The URL
6898          */
6899         'url' : function(v){
6900             return url.test(v);
6901         },
6902         /**
6903          * The error text to display when the url validation function returns false
6904          * @type String
6905          */
6906         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
6907         
6908         /**
6909          * The function used to validate alpha values
6910          * @param {String} value The value
6911          */
6912         'alpha' : function(v){
6913             return alpha.test(v);
6914         },
6915         /**
6916          * The error text to display when the alpha validation function returns false
6917          * @type String
6918          */
6919         'alphaText' : 'This field should only contain letters and _',
6920         /**
6921          * The keystroke filter mask to be applied on alpha input
6922          * @type RegExp
6923          */
6924         'alphaMask' : /[a-z_]/i,
6925
6926         /**
6927          * The function used to validate alphanumeric values
6928          * @param {String} value The value
6929          */
6930         'alphanum' : function(v){
6931             return alphanum.test(v);
6932         },
6933         /**
6934          * The error text to display when the alphanumeric validation function returns false
6935          * @type String
6936          */
6937         'alphanumText' : 'This field should only contain letters, numbers and _',
6938         /**
6939          * The keystroke filter mask to be applied on alphanumeric input
6940          * @type RegExp
6941          */
6942         'alphanumMask' : /[a-z0-9_]/i
6943     };
6944 }();/*
6945  * - LGPL
6946  *
6947  * Input
6948  * 
6949  */
6950
6951 /**
6952  * @class Roo.bootstrap.Input
6953  * @extends Roo.bootstrap.Component
6954  * Bootstrap Input class
6955  * @cfg {Boolean} disabled is it disabled
6956  * @cfg {String} fieldLabel - the label associated
6957  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
6958  * @cfg {String} name name of the input
6959  * @cfg {string} fieldLabel - the label associated
6960  * @cfg {string}  inputType - input / file submit ...
6961  * @cfg {string} placeholder - placeholder to put in text.
6962  * @cfg {string}  before - input group add on before
6963  * @cfg {string} after - input group add on after
6964  * @cfg {string} size - (lg|sm) or leave empty..
6965  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
6966  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
6967  * @cfg {Number} md colspan out of 12 for computer-sized screens
6968  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
6969  * @cfg {string} value default value of the input
6970  * @cfg {Number} labelWidth set the width of label (0-12)
6971  * @cfg {String} labelAlign (top|left)
6972  * @cfg {Boolean} readOnly Specifies that the field should be read-only
6973  * @cfg {String} align (left|center|right) Default left
6974  * 
6975  * 
6976  * @constructor
6977  * Create a new Input
6978  * @param {Object} config The config object
6979  */
6980
6981 Roo.bootstrap.Input = function(config){
6982     Roo.bootstrap.Input.superclass.constructor.call(this, config);
6983    
6984         this.addEvents({
6985             /**
6986              * @event focus
6987              * Fires when this field receives input focus.
6988              * @param {Roo.form.Field} this
6989              */
6990             focus : true,
6991             /**
6992              * @event blur
6993              * Fires when this field loses input focus.
6994              * @param {Roo.form.Field} this
6995              */
6996             blur : true,
6997             /**
6998              * @event specialkey
6999              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
7000              * {@link Roo.EventObject#getKey} to determine which key was pressed.
7001              * @param {Roo.form.Field} this
7002              * @param {Roo.EventObject} e The event object
7003              */
7004             specialkey : true,
7005             /**
7006              * @event change
7007              * Fires just before the field blurs if the field value has changed.
7008              * @param {Roo.form.Field} this
7009              * @param {Mixed} newValue The new value
7010              * @param {Mixed} oldValue The original value
7011              */
7012             change : true,
7013             /**
7014              * @event invalid
7015              * Fires after the field has been marked as invalid.
7016              * @param {Roo.form.Field} this
7017              * @param {String} msg The validation message
7018              */
7019             invalid : true,
7020             /**
7021              * @event valid
7022              * Fires after the field has been validated with no errors.
7023              * @param {Roo.form.Field} this
7024              */
7025             valid : true,
7026              /**
7027              * @event keyup
7028              * Fires after the key up
7029              * @param {Roo.form.Field} this
7030              * @param {Roo.EventObject}  e The event Object
7031              */
7032             keyup : true
7033         });
7034 };
7035
7036 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
7037      /**
7038      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
7039       automatic validation (defaults to "keyup").
7040      */
7041     validationEvent : "keyup",
7042      /**
7043      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
7044      */
7045     validateOnBlur : true,
7046     /**
7047      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
7048      */
7049     validationDelay : 250,
7050      /**
7051      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
7052      */
7053     focusClass : "x-form-focus",  // not needed???
7054     
7055        
7056     /**
7057      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
7058      */
7059     invalidClass : "has-error",
7060     
7061     /**
7062      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
7063      */
7064     selectOnFocus : false,
7065     
7066      /**
7067      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
7068      */
7069     maskRe : null,
7070        /**
7071      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
7072      */
7073     vtype : null,
7074     
7075       /**
7076      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
7077      */
7078     disableKeyFilter : false,
7079     
7080        /**
7081      * @cfg {Boolean} disabled True to disable the field (defaults to false).
7082      */
7083     disabled : false,
7084      /**
7085      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
7086      */
7087     allowBlank : true,
7088     /**
7089      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
7090      */
7091     blankText : "This field is required",
7092     
7093      /**
7094      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
7095      */
7096     minLength : 0,
7097     /**
7098      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
7099      */
7100     maxLength : Number.MAX_VALUE,
7101     /**
7102      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
7103      */
7104     minLengthText : "The minimum length for this field is {0}",
7105     /**
7106      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
7107      */
7108     maxLengthText : "The maximum length for this field is {0}",
7109   
7110     
7111     /**
7112      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
7113      * If available, this function will be called only after the basic validators all return true, and will be passed the
7114      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
7115      */
7116     validator : null,
7117     /**
7118      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
7119      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7120      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
7121      */
7122     regex : null,
7123     /**
7124      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7125      */
7126     regexText : "",
7127     
7128     
7129     
7130     fieldLabel : '',
7131     inputType : 'text',
7132     
7133     name : false,
7134     placeholder: false,
7135     before : false,
7136     after : false,
7137     size : false,
7138     // private
7139     hasFocus : false,
7140     preventMark: false,
7141     isFormField : true,
7142     value : '',
7143     labelWidth : 2,
7144     labelAlign : false,
7145     readOnly : false,
7146     align : false,
7147     formatedValue : false,
7148     
7149     parentLabelAlign : function()
7150     {
7151         var parent = this;
7152         while (parent.parent()) {
7153             parent = parent.parent();
7154             if (typeof(parent.labelAlign) !='undefined') {
7155                 return parent.labelAlign;
7156             }
7157         }
7158         return 'left';
7159         
7160     },
7161     
7162     getAutoCreate : function(){
7163         
7164         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7165         
7166         var id = Roo.id();
7167         
7168         var cfg = {};
7169         
7170         if(this.inputType != 'hidden'){
7171             cfg.cls = 'form-group' //input-group
7172         }
7173         
7174         var input =  {
7175             tag: 'input',
7176             id : id,
7177             type : this.inputType,
7178             value : this.value,
7179             cls : 'form-control',
7180             placeholder : this.placeholder || ''
7181             
7182         };
7183         
7184         if(this.align){
7185             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7186         }
7187         
7188         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7189             input.maxLength = this.maxLength;
7190         }
7191         
7192         if (this.disabled) {
7193             input.disabled=true;
7194         }
7195         
7196         if (this.readOnly) {
7197             input.readonly=true;
7198         }
7199         
7200         if (this.name) {
7201             input.name = this.name;
7202         }
7203         if (this.size) {
7204             input.cls += ' input-' + this.size;
7205         }
7206         var settings=this;
7207         ['xs','sm','md','lg'].map(function(size){
7208             if (settings[size]) {
7209                 cfg.cls += ' col-' + size + '-' + settings[size];
7210             }
7211         });
7212         
7213         var inputblock = input;
7214         
7215         if (this.before || this.after) {
7216             
7217             inputblock = {
7218                 cls : 'input-group',
7219                 cn :  [] 
7220             };
7221             if (this.before && typeof(this.before) == 'string') {
7222                 
7223                 inputblock.cn.push({
7224                     tag :'span',
7225                     cls : 'roo-input-before input-group-addon',
7226                     html : this.before
7227                 });
7228             }
7229             if (this.before && typeof(this.before) == 'object') {
7230                 this.before = Roo.factory(this.before);
7231                 Roo.log(this.before);
7232                 inputblock.cn.push({
7233                     tag :'span',
7234                     cls : 'roo-input-before input-group-' +
7235                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7236                 });
7237             }
7238             
7239             inputblock.cn.push(input);
7240             
7241             if (this.after && typeof(this.after) == 'string') {
7242                 inputblock.cn.push({
7243                     tag :'span',
7244                     cls : 'roo-input-after input-group-addon',
7245                     html : this.after
7246                 });
7247             }
7248             if (this.after && typeof(this.after) == 'object') {
7249                 this.after = Roo.factory(this.after);
7250                 Roo.log(this.after);
7251                 inputblock.cn.push({
7252                     tag :'span',
7253                     cls : 'roo-input-after input-group-' +
7254                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7255                 });
7256             }
7257         };
7258         
7259         if (align ==='left' && this.fieldLabel.length) {
7260                 Roo.log("left and has label");
7261                 cfg.cn = [
7262                     
7263                     {
7264                         tag: 'label',
7265                         'for' :  id,
7266                         cls : 'control-label col-sm-' + this.labelWidth,
7267                         html : this.fieldLabel
7268                         
7269                     },
7270                     {
7271                         cls : "col-sm-" + (12 - this.labelWidth), 
7272                         cn: [
7273                             inputblock
7274                         ]
7275                     }
7276                     
7277                 ];
7278         } else if ( this.fieldLabel.length) {
7279                 Roo.log(" label");
7280                  cfg.cn = [
7281                    
7282                     {
7283                         tag: 'label',
7284                         //cls : 'input-group-addon',
7285                         html : this.fieldLabel
7286                         
7287                     },
7288                     
7289                     inputblock
7290                     
7291                 ];
7292
7293         } else {
7294             
7295                 Roo.log(" no label && no align");
7296                 cfg.cn = [
7297                     
7298                         inputblock
7299                     
7300                 ];
7301                 
7302                 
7303         };
7304         Roo.log('input-parentType: ' + this.parentType);
7305         
7306         if (this.parentType === 'Navbar' &&  this.parent().bar) {
7307            cfg.cls += ' navbar-form';
7308            Roo.log(cfg);
7309         }
7310         
7311         return cfg;
7312         
7313     },
7314     /**
7315      * return the real input element.
7316      */
7317     inputEl: function ()
7318     {
7319         return this.el.select('input.form-control',true).first();
7320     },
7321     
7322     tooltipEl : function()
7323     {
7324         return this.inputEl();
7325     },
7326     
7327     setDisabled : function(v)
7328     {
7329         var i  = this.inputEl().dom;
7330         if (!v) {
7331             i.removeAttribute('disabled');
7332             return;
7333             
7334         }
7335         i.setAttribute('disabled','true');
7336     },
7337     initEvents : function()
7338     {
7339           
7340         this.inputEl().on("keydown" , this.fireKey,  this);
7341         this.inputEl().on("focus", this.onFocus,  this);
7342         this.inputEl().on("blur", this.onBlur,  this);
7343         
7344         this.inputEl().relayEvent('keyup', this);
7345
7346         // reference to original value for reset
7347         this.originalValue = this.getValue();
7348         //Roo.form.TextField.superclass.initEvents.call(this);
7349         if(this.validationEvent == 'keyup'){
7350             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
7351             this.inputEl().on('keyup', this.filterValidation, this);
7352         }
7353         else if(this.validationEvent !== false){
7354             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
7355         }
7356         
7357         if(this.selectOnFocus){
7358             this.on("focus", this.preFocus, this);
7359             
7360         }
7361         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
7362             this.inputEl().on("keypress", this.filterKeys, this);
7363         }
7364        /* if(this.grow){
7365             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
7366             this.el.on("click", this.autoSize,  this);
7367         }
7368         */
7369         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
7370             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
7371         }
7372         
7373         if (typeof(this.before) == 'object') {
7374             this.before.render(this.el.select('.roo-input-before',true).first());
7375         }
7376         if (typeof(this.after) == 'object') {
7377             this.after.render(this.el.select('.roo-input-after',true).first());
7378         }
7379         
7380         
7381     },
7382     filterValidation : function(e){
7383         if(!e.isNavKeyPress()){
7384             this.validationTask.delay(this.validationDelay);
7385         }
7386     },
7387      /**
7388      * Validates the field value
7389      * @return {Boolean} True if the value is valid, else false
7390      */
7391     validate : function(){
7392         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
7393         if(this.disabled || this.validateValue(this.getRawValue())){
7394             this.clearInvalid();
7395             return true;
7396         }
7397         return false;
7398     },
7399     
7400     
7401     /**
7402      * Validates a value according to the field's validation rules and marks the field as invalid
7403      * if the validation fails
7404      * @param {Mixed} value The value to validate
7405      * @return {Boolean} True if the value is valid, else false
7406      */
7407     validateValue : function(value){
7408         if(value.length < 1)  { // if it's blank
7409              if(this.allowBlank){
7410                 this.clearInvalid();
7411                 return true;
7412              }else{
7413                 this.markInvalid(this.blankText);
7414                 return false;
7415              }
7416         }
7417         if(value.length < this.minLength){
7418             this.markInvalid(String.format(this.minLengthText, this.minLength));
7419             return false;
7420         }
7421         if(value.length > this.maxLength){
7422             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
7423             return false;
7424         }
7425         if(this.vtype){
7426             var vt = Roo.form.VTypes;
7427             if(!vt[this.vtype](value, this)){
7428                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
7429                 return false;
7430             }
7431         }
7432         if(typeof this.validator == "function"){
7433             var msg = this.validator(value);
7434             if(msg !== true){
7435                 this.markInvalid(msg);
7436                 return false;
7437             }
7438         }
7439         if(this.regex && !this.regex.test(value)){
7440             this.markInvalid(this.regexText);
7441             return false;
7442         }
7443         return true;
7444     },
7445
7446     
7447     
7448      // private
7449     fireKey : function(e){
7450         //Roo.log('field ' + e.getKey());
7451         if(e.isNavKeyPress()){
7452             this.fireEvent("specialkey", this, e);
7453         }
7454     },
7455     focus : function (selectText){
7456         if(this.rendered){
7457             this.inputEl().focus();
7458             if(selectText === true){
7459                 this.inputEl().dom.select();
7460             }
7461         }
7462         return this;
7463     } ,
7464     
7465     onFocus : function(){
7466         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7467            // this.el.addClass(this.focusClass);
7468         }
7469         if(!this.hasFocus){
7470             this.hasFocus = true;
7471             this.startValue = this.getValue();
7472             this.fireEvent("focus", this);
7473         }
7474     },
7475     
7476     beforeBlur : Roo.emptyFn,
7477
7478     
7479     // private
7480     onBlur : function(){
7481         this.beforeBlur();
7482         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7483             //this.el.removeClass(this.focusClass);
7484         }
7485         this.hasFocus = false;
7486         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
7487             this.validate();
7488         }
7489         var v = this.getValue();
7490         if(String(v) !== String(this.startValue)){
7491             this.fireEvent('change', this, v, this.startValue);
7492         }
7493         this.fireEvent("blur", this);
7494     },
7495     
7496     /**
7497      * Resets the current field value to the originally loaded value and clears any validation messages
7498      */
7499     reset : function(){
7500         this.setValue(this.originalValue);
7501         this.clearInvalid();
7502     },
7503      /**
7504      * Returns the name of the field
7505      * @return {Mixed} name The name field
7506      */
7507     getName: function(){
7508         return this.name;
7509     },
7510      /**
7511      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
7512      * @return {Mixed} value The field value
7513      */
7514     getValue : function(){
7515         
7516         var v = this.inputEl().getValue();
7517         
7518         return v;
7519     },
7520     /**
7521      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
7522      * @return {Mixed} value The field value
7523      */
7524     getRawValue : function(){
7525         var v = this.inputEl().getValue();
7526         
7527         return v;
7528     },
7529     
7530     /**
7531      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
7532      * @param {Mixed} value The value to set
7533      */
7534     setRawValue : function(v){
7535         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7536     },
7537     
7538     selectText : function(start, end){
7539         var v = this.getRawValue();
7540         if(v.length > 0){
7541             start = start === undefined ? 0 : start;
7542             end = end === undefined ? v.length : end;
7543             var d = this.inputEl().dom;
7544             if(d.setSelectionRange){
7545                 d.setSelectionRange(start, end);
7546             }else if(d.createTextRange){
7547                 var range = d.createTextRange();
7548                 range.moveStart("character", start);
7549                 range.moveEnd("character", v.length-end);
7550                 range.select();
7551             }
7552         }
7553     },
7554     
7555     /**
7556      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
7557      * @param {Mixed} value The value to set
7558      */
7559     setValue : function(v){
7560         this.value = v;
7561         if(this.rendered){
7562             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7563             this.validate();
7564         }
7565     },
7566     
7567     /*
7568     processValue : function(value){
7569         if(this.stripCharsRe){
7570             var newValue = value.replace(this.stripCharsRe, '');
7571             if(newValue !== value){
7572                 this.setRawValue(newValue);
7573                 return newValue;
7574             }
7575         }
7576         return value;
7577     },
7578   */
7579     preFocus : function(){
7580         
7581         if(this.selectOnFocus){
7582             this.inputEl().dom.select();
7583         }
7584     },
7585     filterKeys : function(e){
7586         var k = e.getKey();
7587         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
7588             return;
7589         }
7590         var c = e.getCharCode(), cc = String.fromCharCode(c);
7591         if(Roo.isIE && (e.isSpecialKey() || !cc)){
7592             return;
7593         }
7594         if(!this.maskRe.test(cc)){
7595             e.stopEvent();
7596         }
7597     },
7598      /**
7599      * Clear any invalid styles/messages for this field
7600      */
7601     clearInvalid : function(){
7602         
7603         if(!this.el || this.preventMark){ // not rendered
7604             return;
7605         }
7606         this.el.removeClass(this.invalidClass);
7607         /*
7608         switch(this.msgTarget){
7609             case 'qtip':
7610                 this.el.dom.qtip = '';
7611                 break;
7612             case 'title':
7613                 this.el.dom.title = '';
7614                 break;
7615             case 'under':
7616                 if(this.errorEl){
7617                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
7618                 }
7619                 break;
7620             case 'side':
7621                 if(this.errorIcon){
7622                     this.errorIcon.dom.qtip = '';
7623                     this.errorIcon.hide();
7624                     this.un('resize', this.alignErrorIcon, this);
7625                 }
7626                 break;
7627             default:
7628                 var t = Roo.getDom(this.msgTarget);
7629                 t.innerHTML = '';
7630                 t.style.display = 'none';
7631                 break;
7632         }
7633         */
7634         this.fireEvent('valid', this);
7635     },
7636      /**
7637      * Mark this field as invalid
7638      * @param {String} msg The validation message
7639      */
7640     markInvalid : function(msg){
7641         if(!this.el  || this.preventMark){ // not rendered
7642             return;
7643         }
7644         this.el.addClass(this.invalidClass);
7645         /*
7646         msg = msg || this.invalidText;
7647         switch(this.msgTarget){
7648             case 'qtip':
7649                 this.el.dom.qtip = msg;
7650                 this.el.dom.qclass = 'x-form-invalid-tip';
7651                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
7652                     Roo.QuickTips.enable();
7653                 }
7654                 break;
7655             case 'title':
7656                 this.el.dom.title = msg;
7657                 break;
7658             case 'under':
7659                 if(!this.errorEl){
7660                     var elp = this.el.findParent('.x-form-element', 5, true);
7661                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
7662                     this.errorEl.setWidth(elp.getWidth(true)-20);
7663                 }
7664                 this.errorEl.update(msg);
7665                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
7666                 break;
7667             case 'side':
7668                 if(!this.errorIcon){
7669                     var elp = this.el.findParent('.x-form-element', 5, true);
7670                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
7671                 }
7672                 this.alignErrorIcon();
7673                 this.errorIcon.dom.qtip = msg;
7674                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
7675                 this.errorIcon.show();
7676                 this.on('resize', this.alignErrorIcon, this);
7677                 break;
7678             default:
7679                 var t = Roo.getDom(this.msgTarget);
7680                 t.innerHTML = msg;
7681                 t.style.display = this.msgDisplay;
7682                 break;
7683         }
7684         */
7685         this.fireEvent('invalid', this, msg);
7686     },
7687     // private
7688     SafariOnKeyDown : function(event)
7689     {
7690         // this is a workaround for a password hang bug on chrome/ webkit.
7691         
7692         var isSelectAll = false;
7693         
7694         if(this.inputEl().dom.selectionEnd > 0){
7695             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
7696         }
7697         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
7698             event.preventDefault();
7699             this.setValue('');
7700             return;
7701         }
7702         
7703         if(isSelectAll  && event.getCharCode() > 31){ // not backspace and delete key
7704             
7705             event.preventDefault();
7706             // this is very hacky as keydown always get's upper case.
7707             //
7708             var cc = String.fromCharCode(event.getCharCode());
7709             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
7710             
7711         }
7712     },
7713     adjustWidth : function(tag, w){
7714         tag = tag.toLowerCase();
7715         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
7716             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
7717                 if(tag == 'input'){
7718                     return w + 2;
7719                 }
7720                 if(tag == 'textarea'){
7721                     return w-2;
7722                 }
7723             }else if(Roo.isOpera){
7724                 if(tag == 'input'){
7725                     return w + 2;
7726                 }
7727                 if(tag == 'textarea'){
7728                     return w-2;
7729                 }
7730             }
7731         }
7732         return w;
7733     }
7734     
7735 });
7736
7737  
7738 /*
7739  * - LGPL
7740  *
7741  * Input
7742  * 
7743  */
7744
7745 /**
7746  * @class Roo.bootstrap.TextArea
7747  * @extends Roo.bootstrap.Input
7748  * Bootstrap TextArea class
7749  * @cfg {Number} cols Specifies the visible width of a text area
7750  * @cfg {Number} rows Specifies the visible number of lines in a text area
7751  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
7752  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
7753  * @cfg {string} html text
7754  * 
7755  * @constructor
7756  * Create a new TextArea
7757  * @param {Object} config The config object
7758  */
7759
7760 Roo.bootstrap.TextArea = function(config){
7761     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
7762    
7763 };
7764
7765 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
7766      
7767     cols : false,
7768     rows : 5,
7769     readOnly : false,
7770     warp : 'soft',
7771     resize : false,
7772     value: false,
7773     html: false,
7774     
7775     getAutoCreate : function(){
7776         
7777         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7778         
7779         var id = Roo.id();
7780         
7781         var cfg = {};
7782         
7783         var input =  {
7784             tag: 'textarea',
7785             id : id,
7786             warp : this.warp,
7787             rows : this.rows,
7788             value : this.value || '',
7789             html: this.html || '',
7790             cls : 'form-control',
7791             placeholder : this.placeholder || '' 
7792             
7793         };
7794         
7795         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7796             input.maxLength = this.maxLength;
7797         }
7798         
7799         if(this.resize){
7800             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
7801         }
7802         
7803         if(this.cols){
7804             input.cols = this.cols;
7805         }
7806         
7807         if (this.readOnly) {
7808             input.readonly = true;
7809         }
7810         
7811         if (this.name) {
7812             input.name = this.name;
7813         }
7814         
7815         if (this.size) {
7816             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
7817         }
7818         
7819         var settings=this;
7820         ['xs','sm','md','lg'].map(function(size){
7821             if (settings[size]) {
7822                 cfg.cls += ' col-' + size + '-' + settings[size];
7823             }
7824         });
7825         
7826         var inputblock = input;
7827         
7828         if (this.before || this.after) {
7829             
7830             inputblock = {
7831                 cls : 'input-group',
7832                 cn :  [] 
7833             };
7834             if (this.before) {
7835                 inputblock.cn.push({
7836                     tag :'span',
7837                     cls : 'input-group-addon',
7838                     html : this.before
7839                 });
7840             }
7841             inputblock.cn.push(input);
7842             if (this.after) {
7843                 inputblock.cn.push({
7844                     tag :'span',
7845                     cls : 'input-group-addon',
7846                     html : this.after
7847                 });
7848             }
7849             
7850         }
7851         
7852         if (align ==='left' && this.fieldLabel.length) {
7853                 Roo.log("left and has label");
7854                 cfg.cn = [
7855                     
7856                     {
7857                         tag: 'label',
7858                         'for' :  id,
7859                         cls : 'control-label col-sm-' + this.labelWidth,
7860                         html : this.fieldLabel
7861                         
7862                     },
7863                     {
7864                         cls : "col-sm-" + (12 - this.labelWidth), 
7865                         cn: [
7866                             inputblock
7867                         ]
7868                     }
7869                     
7870                 ];
7871         } else if ( this.fieldLabel.length) {
7872                 Roo.log(" label");
7873                  cfg.cn = [
7874                    
7875                     {
7876                         tag: 'label',
7877                         //cls : 'input-group-addon',
7878                         html : this.fieldLabel
7879                         
7880                     },
7881                     
7882                     inputblock
7883                     
7884                 ];
7885
7886         } else {
7887             
7888                    Roo.log(" no label && no align");
7889                 cfg.cn = [
7890                     
7891                         inputblock
7892                     
7893                 ];
7894                 
7895                 
7896         }
7897         
7898         if (this.disabled) {
7899             input.disabled=true;
7900         }
7901         
7902         return cfg;
7903         
7904     },
7905     /**
7906      * return the real textarea element.
7907      */
7908     inputEl: function ()
7909     {
7910         return this.el.select('textarea.form-control',true).first();
7911     }
7912 });
7913
7914  
7915 /*
7916  * - LGPL
7917  *
7918  * trigger field - base class for combo..
7919  * 
7920  */
7921  
7922 /**
7923  * @class Roo.bootstrap.TriggerField
7924  * @extends Roo.bootstrap.Input
7925  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
7926  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
7927  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
7928  * for which you can provide a custom implementation.  For example:
7929  * <pre><code>
7930 var trigger = new Roo.bootstrap.TriggerField();
7931 trigger.onTriggerClick = myTriggerFn;
7932 trigger.applyTo('my-field');
7933 </code></pre>
7934  *
7935  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
7936  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
7937  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
7938  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
7939  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
7940
7941  * @constructor
7942  * Create a new TriggerField.
7943  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
7944  * to the base TextField)
7945  */
7946 Roo.bootstrap.TriggerField = function(config){
7947     this.mimicing = false;
7948     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
7949 };
7950
7951 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
7952     /**
7953      * @cfg {String} triggerClass A CSS class to apply to the trigger
7954      */
7955      /**
7956      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
7957      */
7958     hideTrigger:false,
7959
7960     /** @cfg {Boolean} grow @hide */
7961     /** @cfg {Number} growMin @hide */
7962     /** @cfg {Number} growMax @hide */
7963
7964     /**
7965      * @hide 
7966      * @method
7967      */
7968     autoSize: Roo.emptyFn,
7969     // private
7970     monitorTab : true,
7971     // private
7972     deferHeight : true,
7973
7974     
7975     actionMode : 'wrap',
7976     
7977     caret : false,
7978     
7979     
7980     getAutoCreate : function(){
7981        
7982         var align = this.labelAlign || this.parentLabelAlign();
7983         
7984         var id = Roo.id();
7985         
7986         var cfg = {
7987             cls: 'form-group' //input-group
7988         };
7989         
7990         
7991         var input =  {
7992             tag: 'input',
7993             id : id,
7994             type : this.inputType,
7995             cls : 'form-control',
7996             autocomplete: 'false',
7997             placeholder : this.placeholder || '' 
7998             
7999         };
8000         if (this.name) {
8001             input.name = this.name;
8002         }
8003         if (this.size) {
8004             input.cls += ' input-' + this.size;
8005         }
8006         
8007         if (this.disabled) {
8008             input.disabled=true;
8009         }
8010         
8011         var inputblock = input;
8012         
8013         if (this.before || this.after) {
8014             
8015             inputblock = {
8016                 cls : 'input-group',
8017                 cn :  [] 
8018             };
8019             if (this.before) {
8020                 inputblock.cn.push({
8021                     tag :'span',
8022                     cls : 'input-group-addon',
8023                     html : this.before
8024                 });
8025             }
8026             inputblock.cn.push(input);
8027             if (this.after) {
8028                 inputblock.cn.push({
8029                     tag :'span',
8030                     cls : 'input-group-addon',
8031                     html : this.after
8032                 });
8033             }
8034             
8035         };
8036         
8037         var box = {
8038             tag: 'div',
8039             cn: [
8040                 {
8041                     tag: 'input',
8042                     type : 'hidden',
8043                     cls: 'form-hidden-field'
8044                 },
8045                 inputblock
8046             ]
8047             
8048         };
8049         
8050         if(this.multiple){
8051             Roo.log('multiple');
8052             
8053             box = {
8054                 tag: 'div',
8055                 cn: [
8056                     {
8057                         tag: 'input',
8058                         type : 'hidden',
8059                         cls: 'form-hidden-field'
8060                     },
8061                     {
8062                         tag: 'ul',
8063                         cls: 'select2-choices',
8064                         cn:[
8065                             {
8066                                 tag: 'li',
8067                                 cls: 'select2-search-field',
8068                                 cn: [
8069
8070                                     inputblock
8071                                 ]
8072                             }
8073                         ]
8074                     }
8075                 ]
8076             }
8077         };
8078         
8079         var combobox = {
8080             cls: 'select2-container input-group',
8081             cn: [
8082                 box
8083 //                {
8084 //                    tag: 'ul',
8085 //                    cls: 'typeahead typeahead-long dropdown-menu',
8086 //                    style: 'display:none'
8087 //                }
8088             ]
8089         };
8090         
8091         if(!this.multiple && this.showToggleBtn){
8092             
8093             var caret = {
8094                         tag: 'span',
8095                         cls: 'caret'
8096              };
8097             if (this.caret != false) {
8098                 caret = {
8099                      tag: 'i',
8100                      cls: 'fa fa-' + this.caret
8101                 };
8102                 
8103             }
8104             
8105             combobox.cn.push({
8106                 tag :'span',
8107                 cls : 'input-group-addon btn dropdown-toggle',
8108                 cn : [
8109                     caret,
8110                     {
8111                         tag: 'span',
8112                         cls: 'combobox-clear',
8113                         cn  : [
8114                             {
8115                                 tag : 'i',
8116                                 cls: 'icon-remove'
8117                             }
8118                         ]
8119                     }
8120                 ]
8121
8122             })
8123         }
8124         
8125         if(this.multiple){
8126             combobox.cls += ' select2-container-multi';
8127         }
8128         
8129         if (align ==='left' && this.fieldLabel.length) {
8130             
8131                 Roo.log("left and has label");
8132                 cfg.cn = [
8133                     
8134                     {
8135                         tag: 'label',
8136                         'for' :  id,
8137                         cls : 'control-label col-sm-' + this.labelWidth,
8138                         html : this.fieldLabel
8139                         
8140                     },
8141                     {
8142                         cls : "col-sm-" + (12 - this.labelWidth), 
8143                         cn: [
8144                             combobox
8145                         ]
8146                     }
8147                     
8148                 ];
8149         } else if ( this.fieldLabel.length) {
8150                 Roo.log(" label");
8151                  cfg.cn = [
8152                    
8153                     {
8154                         tag: 'label',
8155                         //cls : 'input-group-addon',
8156                         html : this.fieldLabel
8157                         
8158                     },
8159                     
8160                     combobox
8161                     
8162                 ];
8163
8164         } else {
8165             
8166                 Roo.log(" no label && no align");
8167                 cfg = combobox
8168                      
8169                 
8170         }
8171          
8172         var settings=this;
8173         ['xs','sm','md','lg'].map(function(size){
8174             if (settings[size]) {
8175                 cfg.cls += ' col-' + size + '-' + settings[size];
8176             }
8177         });
8178         
8179         return cfg;
8180         
8181     },
8182     
8183     
8184     
8185     // private
8186     onResize : function(w, h){
8187 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
8188 //        if(typeof w == 'number'){
8189 //            var x = w - this.trigger.getWidth();
8190 //            this.inputEl().setWidth(this.adjustWidth('input', x));
8191 //            this.trigger.setStyle('left', x+'px');
8192 //        }
8193     },
8194
8195     // private
8196     adjustSize : Roo.BoxComponent.prototype.adjustSize,
8197
8198     // private
8199     getResizeEl : function(){
8200         return this.inputEl();
8201     },
8202
8203     // private
8204     getPositionEl : function(){
8205         return this.inputEl();
8206     },
8207
8208     // private
8209     alignErrorIcon : function(){
8210         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
8211     },
8212
8213     // private
8214     initEvents : function(){
8215         
8216         this.createList();
8217         
8218         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
8219         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
8220         if(!this.multiple && this.showToggleBtn){
8221             this.trigger = this.el.select('span.dropdown-toggle',true).first();
8222             if(this.hideTrigger){
8223                 this.trigger.setDisplayed(false);
8224             }
8225             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
8226         }
8227         
8228         if(this.multiple){
8229             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
8230         }
8231         
8232         //this.trigger.addClassOnOver('x-form-trigger-over');
8233         //this.trigger.addClassOnClick('x-form-trigger-click');
8234         
8235         //if(!this.width){
8236         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
8237         //}
8238     },
8239     
8240     createList : function()
8241     {
8242         this.list = Roo.get(document.body).createChild({
8243             tag: 'ul',
8244             cls: 'typeahead typeahead-long dropdown-menu',
8245             style: 'display:none'
8246         });
8247         
8248         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
8249         
8250     },
8251
8252     // private
8253     initTrigger : function(){
8254        
8255     },
8256
8257     // private
8258     onDestroy : function(){
8259         if(this.trigger){
8260             this.trigger.removeAllListeners();
8261           //  this.trigger.remove();
8262         }
8263         //if(this.wrap){
8264         //    this.wrap.remove();
8265         //}
8266         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
8267     },
8268
8269     // private
8270     onFocus : function(){
8271         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
8272         /*
8273         if(!this.mimicing){
8274             this.wrap.addClass('x-trigger-wrap-focus');
8275             this.mimicing = true;
8276             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
8277             if(this.monitorTab){
8278                 this.el.on("keydown", this.checkTab, this);
8279             }
8280         }
8281         */
8282     },
8283
8284     // private
8285     checkTab : function(e){
8286         if(e.getKey() == e.TAB){
8287             this.triggerBlur();
8288         }
8289     },
8290
8291     // private
8292     onBlur : function(){
8293         // do nothing
8294     },
8295
8296     // private
8297     mimicBlur : function(e, t){
8298         /*
8299         if(!this.wrap.contains(t) && this.validateBlur()){
8300             this.triggerBlur();
8301         }
8302         */
8303     },
8304
8305     // private
8306     triggerBlur : function(){
8307         this.mimicing = false;
8308         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
8309         if(this.monitorTab){
8310             this.el.un("keydown", this.checkTab, this);
8311         }
8312         //this.wrap.removeClass('x-trigger-wrap-focus');
8313         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
8314     },
8315
8316     // private
8317     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
8318     validateBlur : function(e, t){
8319         return true;
8320     },
8321
8322     // private
8323     onDisable : function(){
8324         this.inputEl().dom.disabled = true;
8325         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
8326         //if(this.wrap){
8327         //    this.wrap.addClass('x-item-disabled');
8328         //}
8329     },
8330
8331     // private
8332     onEnable : function(){
8333         this.inputEl().dom.disabled = false;
8334         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
8335         //if(this.wrap){
8336         //    this.el.removeClass('x-item-disabled');
8337         //}
8338     },
8339
8340     // private
8341     onShow : function(){
8342         var ae = this.getActionEl();
8343         
8344         if(ae){
8345             ae.dom.style.display = '';
8346             ae.dom.style.visibility = 'visible';
8347         }
8348     },
8349
8350     // private
8351     
8352     onHide : function(){
8353         var ae = this.getActionEl();
8354         ae.dom.style.display = 'none';
8355     },
8356
8357     /**
8358      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
8359      * by an implementing function.
8360      * @method
8361      * @param {EventObject} e
8362      */
8363     onTriggerClick : Roo.emptyFn
8364 });
8365  /*
8366  * Based on:
8367  * Ext JS Library 1.1.1
8368  * Copyright(c) 2006-2007, Ext JS, LLC.
8369  *
8370  * Originally Released Under LGPL - original licence link has changed is not relivant.
8371  *
8372  * Fork - LGPL
8373  * <script type="text/javascript">
8374  */
8375
8376
8377 /**
8378  * @class Roo.data.SortTypes
8379  * @singleton
8380  * Defines the default sorting (casting?) comparison functions used when sorting data.
8381  */
8382 Roo.data.SortTypes = {
8383     /**
8384      * Default sort that does nothing
8385      * @param {Mixed} s The value being converted
8386      * @return {Mixed} The comparison value
8387      */
8388     none : function(s){
8389         return s;
8390     },
8391     
8392     /**
8393      * The regular expression used to strip tags
8394      * @type {RegExp}
8395      * @property
8396      */
8397     stripTagsRE : /<\/?[^>]+>/gi,
8398     
8399     /**
8400      * Strips all HTML tags to sort on text only
8401      * @param {Mixed} s The value being converted
8402      * @return {String} The comparison value
8403      */
8404     asText : function(s){
8405         return String(s).replace(this.stripTagsRE, "");
8406     },
8407     
8408     /**
8409      * Strips all HTML tags to sort on text only - Case insensitive
8410      * @param {Mixed} s The value being converted
8411      * @return {String} The comparison value
8412      */
8413     asUCText : function(s){
8414         return String(s).toUpperCase().replace(this.stripTagsRE, "");
8415     },
8416     
8417     /**
8418      * Case insensitive string
8419      * @param {Mixed} s The value being converted
8420      * @return {String} The comparison value
8421      */
8422     asUCString : function(s) {
8423         return String(s).toUpperCase();
8424     },
8425     
8426     /**
8427      * Date sorting
8428      * @param {Mixed} s The value being converted
8429      * @return {Number} The comparison value
8430      */
8431     asDate : function(s) {
8432         if(!s){
8433             return 0;
8434         }
8435         if(s instanceof Date){
8436             return s.getTime();
8437         }
8438         return Date.parse(String(s));
8439     },
8440     
8441     /**
8442      * Float sorting
8443      * @param {Mixed} s The value being converted
8444      * @return {Float} The comparison value
8445      */
8446     asFloat : function(s) {
8447         var val = parseFloat(String(s).replace(/,/g, ""));
8448         if(isNaN(val)) val = 0;
8449         return val;
8450     },
8451     
8452     /**
8453      * Integer sorting
8454      * @param {Mixed} s The value being converted
8455      * @return {Number} The comparison value
8456      */
8457     asInt : function(s) {
8458         var val = parseInt(String(s).replace(/,/g, ""));
8459         if(isNaN(val)) val = 0;
8460         return val;
8461     }
8462 };/*
8463  * Based on:
8464  * Ext JS Library 1.1.1
8465  * Copyright(c) 2006-2007, Ext JS, LLC.
8466  *
8467  * Originally Released Under LGPL - original licence link has changed is not relivant.
8468  *
8469  * Fork - LGPL
8470  * <script type="text/javascript">
8471  */
8472
8473 /**
8474 * @class Roo.data.Record
8475  * Instances of this class encapsulate both record <em>definition</em> information, and record
8476  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
8477  * to access Records cached in an {@link Roo.data.Store} object.<br>
8478  * <p>
8479  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
8480  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
8481  * objects.<br>
8482  * <p>
8483  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
8484  * @constructor
8485  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
8486  * {@link #create}. The parameters are the same.
8487  * @param {Array} data An associative Array of data values keyed by the field name.
8488  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
8489  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
8490  * not specified an integer id is generated.
8491  */
8492 Roo.data.Record = function(data, id){
8493     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
8494     this.data = data;
8495 };
8496
8497 /**
8498  * Generate a constructor for a specific record layout.
8499  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
8500  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
8501  * Each field definition object may contain the following properties: <ul>
8502  * <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,
8503  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
8504  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
8505  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
8506  * is being used, then this is a string containing the javascript expression to reference the data relative to 
8507  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
8508  * to the data item relative to the record element. If the mapping expression is the same as the field name,
8509  * this may be omitted.</p></li>
8510  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
8511  * <ul><li>auto (Default, implies no conversion)</li>
8512  * <li>string</li>
8513  * <li>int</li>
8514  * <li>float</li>
8515  * <li>boolean</li>
8516  * <li>date</li></ul></p></li>
8517  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
8518  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
8519  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
8520  * by the Reader into an object that will be stored in the Record. It is passed the
8521  * following parameters:<ul>
8522  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
8523  * </ul></p></li>
8524  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
8525  * </ul>
8526  * <br>usage:<br><pre><code>
8527 var TopicRecord = Roo.data.Record.create(
8528     {name: 'title', mapping: 'topic_title'},
8529     {name: 'author', mapping: 'username'},
8530     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
8531     {name: 'lastPost', mapping: 'post_time', type: 'date'},
8532     {name: 'lastPoster', mapping: 'user2'},
8533     {name: 'excerpt', mapping: 'post_text'}
8534 );
8535
8536 var myNewRecord = new TopicRecord({
8537     title: 'Do my job please',
8538     author: 'noobie',
8539     totalPosts: 1,
8540     lastPost: new Date(),
8541     lastPoster: 'Animal',
8542     excerpt: 'No way dude!'
8543 });
8544 myStore.add(myNewRecord);
8545 </code></pre>
8546  * @method create
8547  * @static
8548  */
8549 Roo.data.Record.create = function(o){
8550     var f = function(){
8551         f.superclass.constructor.apply(this, arguments);
8552     };
8553     Roo.extend(f, Roo.data.Record);
8554     var p = f.prototype;
8555     p.fields = new Roo.util.MixedCollection(false, function(field){
8556         return field.name;
8557     });
8558     for(var i = 0, len = o.length; i < len; i++){
8559         p.fields.add(new Roo.data.Field(o[i]));
8560     }
8561     f.getField = function(name){
8562         return p.fields.get(name);  
8563     };
8564     return f;
8565 };
8566
8567 Roo.data.Record.AUTO_ID = 1000;
8568 Roo.data.Record.EDIT = 'edit';
8569 Roo.data.Record.REJECT = 'reject';
8570 Roo.data.Record.COMMIT = 'commit';
8571
8572 Roo.data.Record.prototype = {
8573     /**
8574      * Readonly flag - true if this record has been modified.
8575      * @type Boolean
8576      */
8577     dirty : false,
8578     editing : false,
8579     error: null,
8580     modified: null,
8581
8582     // private
8583     join : function(store){
8584         this.store = store;
8585     },
8586
8587     /**
8588      * Set the named field to the specified value.
8589      * @param {String} name The name of the field to set.
8590      * @param {Object} value The value to set the field to.
8591      */
8592     set : function(name, value){
8593         if(this.data[name] == value){
8594             return;
8595         }
8596         this.dirty = true;
8597         if(!this.modified){
8598             this.modified = {};
8599         }
8600         if(typeof this.modified[name] == 'undefined'){
8601             this.modified[name] = this.data[name];
8602         }
8603         this.data[name] = value;
8604         if(!this.editing && this.store){
8605             this.store.afterEdit(this);
8606         }       
8607     },
8608
8609     /**
8610      * Get the value of the named field.
8611      * @param {String} name The name of the field to get the value of.
8612      * @return {Object} The value of the field.
8613      */
8614     get : function(name){
8615         return this.data[name]; 
8616     },
8617
8618     // private
8619     beginEdit : function(){
8620         this.editing = true;
8621         this.modified = {}; 
8622     },
8623
8624     // private
8625     cancelEdit : function(){
8626         this.editing = false;
8627         delete this.modified;
8628     },
8629
8630     // private
8631     endEdit : function(){
8632         this.editing = false;
8633         if(this.dirty && this.store){
8634             this.store.afterEdit(this);
8635         }
8636     },
8637
8638     /**
8639      * Usually called by the {@link Roo.data.Store} which owns the Record.
8640      * Rejects all changes made to the Record since either creation, or the last commit operation.
8641      * Modified fields are reverted to their original values.
8642      * <p>
8643      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8644      * of reject operations.
8645      */
8646     reject : function(){
8647         var m = this.modified;
8648         for(var n in m){
8649             if(typeof m[n] != "function"){
8650                 this.data[n] = m[n];
8651             }
8652         }
8653         this.dirty = false;
8654         delete this.modified;
8655         this.editing = false;
8656         if(this.store){
8657             this.store.afterReject(this);
8658         }
8659     },
8660
8661     /**
8662      * Usually called by the {@link Roo.data.Store} which owns the Record.
8663      * Commits all changes made to the Record since either creation, or the last commit operation.
8664      * <p>
8665      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8666      * of commit operations.
8667      */
8668     commit : function(){
8669         this.dirty = false;
8670         delete this.modified;
8671         this.editing = false;
8672         if(this.store){
8673             this.store.afterCommit(this);
8674         }
8675     },
8676
8677     // private
8678     hasError : function(){
8679         return this.error != null;
8680     },
8681
8682     // private
8683     clearError : function(){
8684         this.error = null;
8685     },
8686
8687     /**
8688      * Creates a copy of this record.
8689      * @param {String} id (optional) A new record id if you don't want to use this record's id
8690      * @return {Record}
8691      */
8692     copy : function(newId) {
8693         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
8694     }
8695 };/*
8696  * Based on:
8697  * Ext JS Library 1.1.1
8698  * Copyright(c) 2006-2007, Ext JS, LLC.
8699  *
8700  * Originally Released Under LGPL - original licence link has changed is not relivant.
8701  *
8702  * Fork - LGPL
8703  * <script type="text/javascript">
8704  */
8705
8706
8707
8708 /**
8709  * @class Roo.data.Store
8710  * @extends Roo.util.Observable
8711  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
8712  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
8713  * <p>
8714  * 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
8715  * has no knowledge of the format of the data returned by the Proxy.<br>
8716  * <p>
8717  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
8718  * instances from the data object. These records are cached and made available through accessor functions.
8719  * @constructor
8720  * Creates a new Store.
8721  * @param {Object} config A config object containing the objects needed for the Store to access data,
8722  * and read the data into Records.
8723  */
8724 Roo.data.Store = function(config){
8725     this.data = new Roo.util.MixedCollection(false);
8726     this.data.getKey = function(o){
8727         return o.id;
8728     };
8729     this.baseParams = {};
8730     // private
8731     this.paramNames = {
8732         "start" : "start",
8733         "limit" : "limit",
8734         "sort" : "sort",
8735         "dir" : "dir",
8736         "multisort" : "_multisort"
8737     };
8738
8739     if(config && config.data){
8740         this.inlineData = config.data;
8741         delete config.data;
8742     }
8743
8744     Roo.apply(this, config);
8745     
8746     if(this.reader){ // reader passed
8747         this.reader = Roo.factory(this.reader, Roo.data);
8748         this.reader.xmodule = this.xmodule || false;
8749         if(!this.recordType){
8750             this.recordType = this.reader.recordType;
8751         }
8752         if(this.reader.onMetaChange){
8753             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
8754         }
8755     }
8756
8757     if(this.recordType){
8758         this.fields = this.recordType.prototype.fields;
8759     }
8760     this.modified = [];
8761
8762     this.addEvents({
8763         /**
8764          * @event datachanged
8765          * Fires when the data cache has changed, and a widget which is using this Store
8766          * as a Record cache should refresh its view.
8767          * @param {Store} this
8768          */
8769         datachanged : true,
8770         /**
8771          * @event metachange
8772          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
8773          * @param {Store} this
8774          * @param {Object} meta The JSON metadata
8775          */
8776         metachange : true,
8777         /**
8778          * @event add
8779          * Fires when Records have been added to the Store
8780          * @param {Store} this
8781          * @param {Roo.data.Record[]} records The array of Records added
8782          * @param {Number} index The index at which the record(s) were added
8783          */
8784         add : true,
8785         /**
8786          * @event remove
8787          * Fires when a Record has been removed from the Store
8788          * @param {Store} this
8789          * @param {Roo.data.Record} record The Record that was removed
8790          * @param {Number} index The index at which the record was removed
8791          */
8792         remove : true,
8793         /**
8794          * @event update
8795          * Fires when a Record has been updated
8796          * @param {Store} this
8797          * @param {Roo.data.Record} record The Record that was updated
8798          * @param {String} operation The update operation being performed.  Value may be one of:
8799          * <pre><code>
8800  Roo.data.Record.EDIT
8801  Roo.data.Record.REJECT
8802  Roo.data.Record.COMMIT
8803          * </code></pre>
8804          */
8805         update : true,
8806         /**
8807          * @event clear
8808          * Fires when the data cache has been cleared.
8809          * @param {Store} this
8810          */
8811         clear : true,
8812         /**
8813          * @event beforeload
8814          * Fires before a request is made for a new data object.  If the beforeload handler returns false
8815          * the load action will be canceled.
8816          * @param {Store} this
8817          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8818          */
8819         beforeload : true,
8820         /**
8821          * @event beforeloadadd
8822          * Fires after a new set of Records has been loaded.
8823          * @param {Store} this
8824          * @param {Roo.data.Record[]} records The Records that were loaded
8825          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8826          */
8827         beforeloadadd : true,
8828         /**
8829          * @event load
8830          * Fires after a new set of Records has been loaded, before they are added to the store.
8831          * @param {Store} this
8832          * @param {Roo.data.Record[]} records The Records that were loaded
8833          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8834          * @params {Object} return from reader
8835          */
8836         load : true,
8837         /**
8838          * @event loadexception
8839          * Fires if an exception occurs in the Proxy during loading.
8840          * Called with the signature of the Proxy's "loadexception" event.
8841          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
8842          * 
8843          * @param {Proxy} 
8844          * @param {Object} return from JsonData.reader() - success, totalRecords, records
8845          * @param {Object} load options 
8846          * @param {Object} jsonData from your request (normally this contains the Exception)
8847          */
8848         loadexception : true
8849     });
8850     
8851     if(this.proxy){
8852         this.proxy = Roo.factory(this.proxy, Roo.data);
8853         this.proxy.xmodule = this.xmodule || false;
8854         this.relayEvents(this.proxy,  ["loadexception"]);
8855     }
8856     this.sortToggle = {};
8857     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
8858
8859     Roo.data.Store.superclass.constructor.call(this);
8860
8861     if(this.inlineData){
8862         this.loadData(this.inlineData);
8863         delete this.inlineData;
8864     }
8865 };
8866
8867 Roo.extend(Roo.data.Store, Roo.util.Observable, {
8868      /**
8869     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
8870     * without a remote query - used by combo/forms at present.
8871     */
8872     
8873     /**
8874     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
8875     */
8876     /**
8877     * @cfg {Array} data Inline data to be loaded when the store is initialized.
8878     */
8879     /**
8880     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
8881     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
8882     */
8883     /**
8884     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
8885     * on any HTTP request
8886     */
8887     /**
8888     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
8889     */
8890     /**
8891     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
8892     */
8893     multiSort: false,
8894     /**
8895     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
8896     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
8897     */
8898     remoteSort : false,
8899
8900     /**
8901     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
8902      * loaded or when a record is removed. (defaults to false).
8903     */
8904     pruneModifiedRecords : false,
8905
8906     // private
8907     lastOptions : null,
8908
8909     /**
8910      * Add Records to the Store and fires the add event.
8911      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8912      */
8913     add : function(records){
8914         records = [].concat(records);
8915         for(var i = 0, len = records.length; i < len; i++){
8916             records[i].join(this);
8917         }
8918         var index = this.data.length;
8919         this.data.addAll(records);
8920         this.fireEvent("add", this, records, index);
8921     },
8922
8923     /**
8924      * Remove a Record from the Store and fires the remove event.
8925      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
8926      */
8927     remove : function(record){
8928         var index = this.data.indexOf(record);
8929         this.data.removeAt(index);
8930         if(this.pruneModifiedRecords){
8931             this.modified.remove(record);
8932         }
8933         this.fireEvent("remove", this, record, index);
8934     },
8935
8936     /**
8937      * Remove all Records from the Store and fires the clear event.
8938      */
8939     removeAll : function(){
8940         this.data.clear();
8941         if(this.pruneModifiedRecords){
8942             this.modified = [];
8943         }
8944         this.fireEvent("clear", this);
8945     },
8946
8947     /**
8948      * Inserts Records to the Store at the given index and fires the add event.
8949      * @param {Number} index The start index at which to insert the passed Records.
8950      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8951      */
8952     insert : function(index, records){
8953         records = [].concat(records);
8954         for(var i = 0, len = records.length; i < len; i++){
8955             this.data.insert(index, records[i]);
8956             records[i].join(this);
8957         }
8958         this.fireEvent("add", this, records, index);
8959     },
8960
8961     /**
8962      * Get the index within the cache of the passed Record.
8963      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
8964      * @return {Number} The index of the passed Record. Returns -1 if not found.
8965      */
8966     indexOf : function(record){
8967         return this.data.indexOf(record);
8968     },
8969
8970     /**
8971      * Get the index within the cache of the Record with the passed id.
8972      * @param {String} id The id of the Record to find.
8973      * @return {Number} The index of the Record. Returns -1 if not found.
8974      */
8975     indexOfId : function(id){
8976         return this.data.indexOfKey(id);
8977     },
8978
8979     /**
8980      * Get the Record with the specified id.
8981      * @param {String} id The id of the Record to find.
8982      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
8983      */
8984     getById : function(id){
8985         return this.data.key(id);
8986     },
8987
8988     /**
8989      * Get the Record at the specified index.
8990      * @param {Number} index The index of the Record to find.
8991      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
8992      */
8993     getAt : function(index){
8994         return this.data.itemAt(index);
8995     },
8996
8997     /**
8998      * Returns a range of Records between specified indices.
8999      * @param {Number} startIndex (optional) The starting index (defaults to 0)
9000      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
9001      * @return {Roo.data.Record[]} An array of Records
9002      */
9003     getRange : function(start, end){
9004         return this.data.getRange(start, end);
9005     },
9006
9007     // private
9008     storeOptions : function(o){
9009         o = Roo.apply({}, o);
9010         delete o.callback;
9011         delete o.scope;
9012         this.lastOptions = o;
9013     },
9014
9015     /**
9016      * Loads the Record cache from the configured Proxy using the configured Reader.
9017      * <p>
9018      * If using remote paging, then the first load call must specify the <em>start</em>
9019      * and <em>limit</em> properties in the options.params property to establish the initial
9020      * position within the dataset, and the number of Records to cache on each read from the Proxy.
9021      * <p>
9022      * <strong>It is important to note that for remote data sources, loading is asynchronous,
9023      * and this call will return before the new data has been loaded. Perform any post-processing
9024      * in a callback function, or in a "load" event handler.</strong>
9025      * <p>
9026      * @param {Object} options An object containing properties which control loading options:<ul>
9027      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
9028      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
9029      * passed the following arguments:<ul>
9030      * <li>r : Roo.data.Record[]</li>
9031      * <li>options: Options object from the load call</li>
9032      * <li>success: Boolean success indicator</li></ul></li>
9033      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
9034      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
9035      * </ul>
9036      */
9037     load : function(options){
9038         options = options || {};
9039         if(this.fireEvent("beforeload", this, options) !== false){
9040             this.storeOptions(options);
9041             var p = Roo.apply(options.params || {}, this.baseParams);
9042             // if meta was not loaded from remote source.. try requesting it.
9043             if (!this.reader.metaFromRemote) {
9044                 p._requestMeta = 1;
9045             }
9046             if(this.sortInfo && this.remoteSort){
9047                 var pn = this.paramNames;
9048                 p[pn["sort"]] = this.sortInfo.field;
9049                 p[pn["dir"]] = this.sortInfo.direction;
9050             }
9051             if (this.multiSort) {
9052                 var pn = this.paramNames;
9053                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
9054             }
9055             
9056             this.proxy.load(p, this.reader, this.loadRecords, this, options);
9057         }
9058     },
9059
9060     /**
9061      * Reloads the Record cache from the configured Proxy using the configured Reader and
9062      * the options from the last load operation performed.
9063      * @param {Object} options (optional) An object containing properties which may override the options
9064      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
9065      * the most recently used options are reused).
9066      */
9067     reload : function(options){
9068         this.load(Roo.applyIf(options||{}, this.lastOptions));
9069     },
9070
9071     // private
9072     // Called as a callback by the Reader during a load operation.
9073     loadRecords : function(o, options, success){
9074         if(!o || success === false){
9075             if(success !== false){
9076                 this.fireEvent("load", this, [], options, o);
9077             }
9078             if(options.callback){
9079                 options.callback.call(options.scope || this, [], options, false);
9080             }
9081             return;
9082         }
9083         // if data returned failure - throw an exception.
9084         if (o.success === false) {
9085             // show a message if no listener is registered.
9086             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
9087                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
9088             }
9089             // loadmask wil be hooked into this..
9090             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
9091             return;
9092         }
9093         var r = o.records, t = o.totalRecords || r.length;
9094         
9095         this.fireEvent("beforeloadadd", this, r, options, o);
9096         
9097         if(!options || options.add !== true){
9098             if(this.pruneModifiedRecords){
9099                 this.modified = [];
9100             }
9101             for(var i = 0, len = r.length; i < len; i++){
9102                 r[i].join(this);
9103             }
9104             if(this.snapshot){
9105                 this.data = this.snapshot;
9106                 delete this.snapshot;
9107             }
9108             this.data.clear();
9109             this.data.addAll(r);
9110             this.totalLength = t;
9111             this.applySort();
9112             this.fireEvent("datachanged", this);
9113         }else{
9114             this.totalLength = Math.max(t, this.data.length+r.length);
9115             this.add(r);
9116         }
9117         this.fireEvent("load", this, r, options, o);
9118         if(options.callback){
9119             options.callback.call(options.scope || this, r, options, true);
9120         }
9121     },
9122
9123
9124     /**
9125      * Loads data from a passed data block. A Reader which understands the format of the data
9126      * must have been configured in the constructor.
9127      * @param {Object} data The data block from which to read the Records.  The format of the data expected
9128      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
9129      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
9130      */
9131     loadData : function(o, append){
9132         var r = this.reader.readRecords(o);
9133         this.loadRecords(r, {add: append}, true);
9134     },
9135
9136     /**
9137      * Gets the number of cached records.
9138      * <p>
9139      * <em>If using paging, this may not be the total size of the dataset. If the data object
9140      * used by the Reader contains the dataset size, then the getTotalCount() function returns
9141      * the data set size</em>
9142      */
9143     getCount : function(){
9144         return this.data.length || 0;
9145     },
9146
9147     /**
9148      * Gets the total number of records in the dataset as returned by the server.
9149      * <p>
9150      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
9151      * the dataset size</em>
9152      */
9153     getTotalCount : function(){
9154         return this.totalLength || 0;
9155     },
9156
9157     /**
9158      * Returns the sort state of the Store as an object with two properties:
9159      * <pre><code>
9160  field {String} The name of the field by which the Records are sorted
9161  direction {String} The sort order, "ASC" or "DESC"
9162      * </code></pre>
9163      */
9164     getSortState : function(){
9165         return this.sortInfo;
9166     },
9167
9168     // private
9169     applySort : function(){
9170         if(this.sortInfo && !this.remoteSort){
9171             var s = this.sortInfo, f = s.field;
9172             var st = this.fields.get(f).sortType;
9173             var fn = function(r1, r2){
9174                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
9175                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
9176             };
9177             this.data.sort(s.direction, fn);
9178             if(this.snapshot && this.snapshot != this.data){
9179                 this.snapshot.sort(s.direction, fn);
9180             }
9181         }
9182     },
9183
9184     /**
9185      * Sets the default sort column and order to be used by the next load operation.
9186      * @param {String} fieldName The name of the field to sort by.
9187      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9188      */
9189     setDefaultSort : function(field, dir){
9190         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
9191     },
9192
9193     /**
9194      * Sort the Records.
9195      * If remote sorting is used, the sort is performed on the server, and the cache is
9196      * reloaded. If local sorting is used, the cache is sorted internally.
9197      * @param {String} fieldName The name of the field to sort by.
9198      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9199      */
9200     sort : function(fieldName, dir){
9201         var f = this.fields.get(fieldName);
9202         if(!dir){
9203             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
9204             
9205             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
9206                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
9207             }else{
9208                 dir = f.sortDir;
9209             }
9210         }
9211         this.sortToggle[f.name] = dir;
9212         this.sortInfo = {field: f.name, direction: dir};
9213         if(!this.remoteSort){
9214             this.applySort();
9215             this.fireEvent("datachanged", this);
9216         }else{
9217             this.load(this.lastOptions);
9218         }
9219     },
9220
9221     /**
9222      * Calls the specified function for each of the Records in the cache.
9223      * @param {Function} fn The function to call. The Record is passed as the first parameter.
9224      * Returning <em>false</em> aborts and exits the iteration.
9225      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
9226      */
9227     each : function(fn, scope){
9228         this.data.each(fn, scope);
9229     },
9230
9231     /**
9232      * Gets all records modified since the last commit.  Modified records are persisted across load operations
9233      * (e.g., during paging).
9234      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
9235      */
9236     getModifiedRecords : function(){
9237         return this.modified;
9238     },
9239
9240     // private
9241     createFilterFn : function(property, value, anyMatch){
9242         if(!value.exec){ // not a regex
9243             value = String(value);
9244             if(value.length == 0){
9245                 return false;
9246             }
9247             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
9248         }
9249         return function(r){
9250             return value.test(r.data[property]);
9251         };
9252     },
9253
9254     /**
9255      * Sums the value of <i>property</i> for each record between start and end and returns the result.
9256      * @param {String} property A field on your records
9257      * @param {Number} start The record index to start at (defaults to 0)
9258      * @param {Number} end The last record index to include (defaults to length - 1)
9259      * @return {Number} The sum
9260      */
9261     sum : function(property, start, end){
9262         var rs = this.data.items, v = 0;
9263         start = start || 0;
9264         end = (end || end === 0) ? end : rs.length-1;
9265
9266         for(var i = start; i <= end; i++){
9267             v += (rs[i].data[property] || 0);
9268         }
9269         return v;
9270     },
9271
9272     /**
9273      * Filter the records by a specified property.
9274      * @param {String} field A field on your records
9275      * @param {String/RegExp} value Either a string that the field
9276      * should start with or a RegExp to test against the field
9277      * @param {Boolean} anyMatch True to match any part not just the beginning
9278      */
9279     filter : function(property, value, anyMatch){
9280         var fn = this.createFilterFn(property, value, anyMatch);
9281         return fn ? this.filterBy(fn) : this.clearFilter();
9282     },
9283
9284     /**
9285      * Filter by a function. The specified function will be called with each
9286      * record in this data source. If the function returns true the record is included,
9287      * otherwise it is filtered.
9288      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9289      * @param {Object} scope (optional) The scope of the function (defaults to this)
9290      */
9291     filterBy : function(fn, scope){
9292         this.snapshot = this.snapshot || this.data;
9293         this.data = this.queryBy(fn, scope||this);
9294         this.fireEvent("datachanged", this);
9295     },
9296
9297     /**
9298      * Query the records by a specified property.
9299      * @param {String} field A field on your records
9300      * @param {String/RegExp} value Either a string that the field
9301      * should start with or a RegExp to test against the field
9302      * @param {Boolean} anyMatch True to match any part not just the beginning
9303      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9304      */
9305     query : function(property, value, anyMatch){
9306         var fn = this.createFilterFn(property, value, anyMatch);
9307         return fn ? this.queryBy(fn) : this.data.clone();
9308     },
9309
9310     /**
9311      * Query by a function. The specified function will be called with each
9312      * record in this data source. If the function returns true the record is included
9313      * in the results.
9314      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9315      * @param {Object} scope (optional) The scope of the function (defaults to this)
9316       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9317      **/
9318     queryBy : function(fn, scope){
9319         var data = this.snapshot || this.data;
9320         return data.filterBy(fn, scope||this);
9321     },
9322
9323     /**
9324      * Collects unique values for a particular dataIndex from this store.
9325      * @param {String} dataIndex The property to collect
9326      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
9327      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
9328      * @return {Array} An array of the unique values
9329      **/
9330     collect : function(dataIndex, allowNull, bypassFilter){
9331         var d = (bypassFilter === true && this.snapshot) ?
9332                 this.snapshot.items : this.data.items;
9333         var v, sv, r = [], l = {};
9334         for(var i = 0, len = d.length; i < len; i++){
9335             v = d[i].data[dataIndex];
9336             sv = String(v);
9337             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
9338                 l[sv] = true;
9339                 r[r.length] = v;
9340             }
9341         }
9342         return r;
9343     },
9344
9345     /**
9346      * Revert to a view of the Record cache with no filtering applied.
9347      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
9348      */
9349     clearFilter : function(suppressEvent){
9350         if(this.snapshot && this.snapshot != this.data){
9351             this.data = this.snapshot;
9352             delete this.snapshot;
9353             if(suppressEvent !== true){
9354                 this.fireEvent("datachanged", this);
9355             }
9356         }
9357     },
9358
9359     // private
9360     afterEdit : function(record){
9361         if(this.modified.indexOf(record) == -1){
9362             this.modified.push(record);
9363         }
9364         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
9365     },
9366     
9367     // private
9368     afterReject : function(record){
9369         this.modified.remove(record);
9370         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
9371     },
9372
9373     // private
9374     afterCommit : function(record){
9375         this.modified.remove(record);
9376         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
9377     },
9378
9379     /**
9380      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
9381      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
9382      */
9383     commitChanges : function(){
9384         var m = this.modified.slice(0);
9385         this.modified = [];
9386         for(var i = 0, len = m.length; i < len; i++){
9387             m[i].commit();
9388         }
9389     },
9390
9391     /**
9392      * Cancel outstanding changes on all changed records.
9393      */
9394     rejectChanges : function(){
9395         var m = this.modified.slice(0);
9396         this.modified = [];
9397         for(var i = 0, len = m.length; i < len; i++){
9398             m[i].reject();
9399         }
9400     },
9401
9402     onMetaChange : function(meta, rtype, o){
9403         this.recordType = rtype;
9404         this.fields = rtype.prototype.fields;
9405         delete this.snapshot;
9406         this.sortInfo = meta.sortInfo || this.sortInfo;
9407         this.modified = [];
9408         this.fireEvent('metachange', this, this.reader.meta);
9409     },
9410     
9411     moveIndex : function(data, type)
9412     {
9413         var index = this.indexOf(data);
9414         
9415         var newIndex = index + type;
9416         
9417         this.remove(data);
9418         
9419         this.insert(newIndex, data);
9420         
9421     }
9422 });/*
9423  * Based on:
9424  * Ext JS Library 1.1.1
9425  * Copyright(c) 2006-2007, Ext JS, LLC.
9426  *
9427  * Originally Released Under LGPL - original licence link has changed is not relivant.
9428  *
9429  * Fork - LGPL
9430  * <script type="text/javascript">
9431  */
9432
9433 /**
9434  * @class Roo.data.SimpleStore
9435  * @extends Roo.data.Store
9436  * Small helper class to make creating Stores from Array data easier.
9437  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
9438  * @cfg {Array} fields An array of field definition objects, or field name strings.
9439  * @cfg {Array} data The multi-dimensional array of data
9440  * @constructor
9441  * @param {Object} config
9442  */
9443 Roo.data.SimpleStore = function(config){
9444     Roo.data.SimpleStore.superclass.constructor.call(this, {
9445         isLocal : true,
9446         reader: new Roo.data.ArrayReader({
9447                 id: config.id
9448             },
9449             Roo.data.Record.create(config.fields)
9450         ),
9451         proxy : new Roo.data.MemoryProxy(config.data)
9452     });
9453     this.load();
9454 };
9455 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
9456  * Based on:
9457  * Ext JS Library 1.1.1
9458  * Copyright(c) 2006-2007, Ext JS, LLC.
9459  *
9460  * Originally Released Under LGPL - original licence link has changed is not relivant.
9461  *
9462  * Fork - LGPL
9463  * <script type="text/javascript">
9464  */
9465
9466 /**
9467 /**
9468  * @extends Roo.data.Store
9469  * @class Roo.data.JsonStore
9470  * Small helper class to make creating Stores for JSON data easier. <br/>
9471 <pre><code>
9472 var store = new Roo.data.JsonStore({
9473     url: 'get-images.php',
9474     root: 'images',
9475     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
9476 });
9477 </code></pre>
9478  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
9479  * JsonReader and HttpProxy (unless inline data is provided).</b>
9480  * @cfg {Array} fields An array of field definition objects, or field name strings.
9481  * @constructor
9482  * @param {Object} config
9483  */
9484 Roo.data.JsonStore = function(c){
9485     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
9486         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
9487         reader: new Roo.data.JsonReader(c, c.fields)
9488     }));
9489 };
9490 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
9491  * Based on:
9492  * Ext JS Library 1.1.1
9493  * Copyright(c) 2006-2007, Ext JS, LLC.
9494  *
9495  * Originally Released Under LGPL - original licence link has changed is not relivant.
9496  *
9497  * Fork - LGPL
9498  * <script type="text/javascript">
9499  */
9500
9501  
9502 Roo.data.Field = function(config){
9503     if(typeof config == "string"){
9504         config = {name: config};
9505     }
9506     Roo.apply(this, config);
9507     
9508     if(!this.type){
9509         this.type = "auto";
9510     }
9511     
9512     var st = Roo.data.SortTypes;
9513     // named sortTypes are supported, here we look them up
9514     if(typeof this.sortType == "string"){
9515         this.sortType = st[this.sortType];
9516     }
9517     
9518     // set default sortType for strings and dates
9519     if(!this.sortType){
9520         switch(this.type){
9521             case "string":
9522                 this.sortType = st.asUCString;
9523                 break;
9524             case "date":
9525                 this.sortType = st.asDate;
9526                 break;
9527             default:
9528                 this.sortType = st.none;
9529         }
9530     }
9531
9532     // define once
9533     var stripRe = /[\$,%]/g;
9534
9535     // prebuilt conversion function for this field, instead of
9536     // switching every time we're reading a value
9537     if(!this.convert){
9538         var cv, dateFormat = this.dateFormat;
9539         switch(this.type){
9540             case "":
9541             case "auto":
9542             case undefined:
9543                 cv = function(v){ return v; };
9544                 break;
9545             case "string":
9546                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
9547                 break;
9548             case "int":
9549                 cv = function(v){
9550                     return v !== undefined && v !== null && v !== '' ?
9551                            parseInt(String(v).replace(stripRe, ""), 10) : '';
9552                     };
9553                 break;
9554             case "float":
9555                 cv = function(v){
9556                     return v !== undefined && v !== null && v !== '' ?
9557                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
9558                     };
9559                 break;
9560             case "bool":
9561             case "boolean":
9562                 cv = function(v){ return v === true || v === "true" || v == 1; };
9563                 break;
9564             case "date":
9565                 cv = function(v){
9566                     if(!v){
9567                         return '';
9568                     }
9569                     if(v instanceof Date){
9570                         return v;
9571                     }
9572                     if(dateFormat){
9573                         if(dateFormat == "timestamp"){
9574                             return new Date(v*1000);
9575                         }
9576                         return Date.parseDate(v, dateFormat);
9577                     }
9578                     var parsed = Date.parse(v);
9579                     return parsed ? new Date(parsed) : null;
9580                 };
9581              break;
9582             
9583         }
9584         this.convert = cv;
9585     }
9586 };
9587
9588 Roo.data.Field.prototype = {
9589     dateFormat: null,
9590     defaultValue: "",
9591     mapping: null,
9592     sortType : null,
9593     sortDir : "ASC"
9594 };/*
9595  * Based on:
9596  * Ext JS Library 1.1.1
9597  * Copyright(c) 2006-2007, Ext JS, LLC.
9598  *
9599  * Originally Released Under LGPL - original licence link has changed is not relivant.
9600  *
9601  * Fork - LGPL
9602  * <script type="text/javascript">
9603  */
9604  
9605 // Base class for reading structured data from a data source.  This class is intended to be
9606 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
9607
9608 /**
9609  * @class Roo.data.DataReader
9610  * Base class for reading structured data from a data source.  This class is intended to be
9611  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
9612  */
9613
9614 Roo.data.DataReader = function(meta, recordType){
9615     
9616     this.meta = meta;
9617     
9618     this.recordType = recordType instanceof Array ? 
9619         Roo.data.Record.create(recordType) : recordType;
9620 };
9621
9622 Roo.data.DataReader.prototype = {
9623      /**
9624      * Create an empty record
9625      * @param {Object} data (optional) - overlay some values
9626      * @return {Roo.data.Record} record created.
9627      */
9628     newRow :  function(d) {
9629         var da =  {};
9630         this.recordType.prototype.fields.each(function(c) {
9631             switch( c.type) {
9632                 case 'int' : da[c.name] = 0; break;
9633                 case 'date' : da[c.name] = new Date(); break;
9634                 case 'float' : da[c.name] = 0.0; break;
9635                 case 'boolean' : da[c.name] = false; break;
9636                 default : da[c.name] = ""; break;
9637             }
9638             
9639         });
9640         return new this.recordType(Roo.apply(da, d));
9641     }
9642     
9643 };/*
9644  * Based on:
9645  * Ext JS Library 1.1.1
9646  * Copyright(c) 2006-2007, Ext JS, LLC.
9647  *
9648  * Originally Released Under LGPL - original licence link has changed is not relivant.
9649  *
9650  * Fork - LGPL
9651  * <script type="text/javascript">
9652  */
9653
9654 /**
9655  * @class Roo.data.DataProxy
9656  * @extends Roo.data.Observable
9657  * This class is an abstract base class for implementations which provide retrieval of
9658  * unformatted data objects.<br>
9659  * <p>
9660  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
9661  * (of the appropriate type which knows how to parse the data object) to provide a block of
9662  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
9663  * <p>
9664  * Custom implementations must implement the load method as described in
9665  * {@link Roo.data.HttpProxy#load}.
9666  */
9667 Roo.data.DataProxy = function(){
9668     this.addEvents({
9669         /**
9670          * @event beforeload
9671          * Fires before a network request is made to retrieve a data object.
9672          * @param {Object} This DataProxy object.
9673          * @param {Object} params The params parameter to the load function.
9674          */
9675         beforeload : true,
9676         /**
9677          * @event load
9678          * Fires before the load method's callback is called.
9679          * @param {Object} This DataProxy object.
9680          * @param {Object} o The data object.
9681          * @param {Object} arg The callback argument object passed to the load function.
9682          */
9683         load : true,
9684         /**
9685          * @event loadexception
9686          * Fires if an Exception occurs during data retrieval.
9687          * @param {Object} This DataProxy object.
9688          * @param {Object} o The data object.
9689          * @param {Object} arg The callback argument object passed to the load function.
9690          * @param {Object} e The Exception.
9691          */
9692         loadexception : true
9693     });
9694     Roo.data.DataProxy.superclass.constructor.call(this);
9695 };
9696
9697 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
9698
9699     /**
9700      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
9701      */
9702 /*
9703  * Based on:
9704  * Ext JS Library 1.1.1
9705  * Copyright(c) 2006-2007, Ext JS, LLC.
9706  *
9707  * Originally Released Under LGPL - original licence link has changed is not relivant.
9708  *
9709  * Fork - LGPL
9710  * <script type="text/javascript">
9711  */
9712 /**
9713  * @class Roo.data.MemoryProxy
9714  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
9715  * to the Reader when its load method is called.
9716  * @constructor
9717  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
9718  */
9719 Roo.data.MemoryProxy = function(data){
9720     if (data.data) {
9721         data = data.data;
9722     }
9723     Roo.data.MemoryProxy.superclass.constructor.call(this);
9724     this.data = data;
9725 };
9726
9727 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
9728     /**
9729      * Load data from the requested source (in this case an in-memory
9730      * data object passed to the constructor), read the data object into
9731      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9732      * process that block using the passed callback.
9733      * @param {Object} params This parameter is not used by the MemoryProxy class.
9734      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9735      * object into a block of Roo.data.Records.
9736      * @param {Function} callback The function into which to pass the block of Roo.data.records.
9737      * The function must be passed <ul>
9738      * <li>The Record block object</li>
9739      * <li>The "arg" argument from the load function</li>
9740      * <li>A boolean success indicator</li>
9741      * </ul>
9742      * @param {Object} scope The scope in which to call the callback
9743      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9744      */
9745     load : function(params, reader, callback, scope, arg){
9746         params = params || {};
9747         var result;
9748         try {
9749             result = reader.readRecords(this.data);
9750         }catch(e){
9751             this.fireEvent("loadexception", this, arg, null, e);
9752             callback.call(scope, null, arg, false);
9753             return;
9754         }
9755         callback.call(scope, result, arg, true);
9756     },
9757     
9758     // private
9759     update : function(params, records){
9760         
9761     }
9762 });/*
9763  * Based on:
9764  * Ext JS Library 1.1.1
9765  * Copyright(c) 2006-2007, Ext JS, LLC.
9766  *
9767  * Originally Released Under LGPL - original licence link has changed is not relivant.
9768  *
9769  * Fork - LGPL
9770  * <script type="text/javascript">
9771  */
9772 /**
9773  * @class Roo.data.HttpProxy
9774  * @extends Roo.data.DataProxy
9775  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
9776  * configured to reference a certain URL.<br><br>
9777  * <p>
9778  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
9779  * from which the running page was served.<br><br>
9780  * <p>
9781  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
9782  * <p>
9783  * Be aware that to enable the browser to parse an XML document, the server must set
9784  * the Content-Type header in the HTTP response to "text/xml".
9785  * @constructor
9786  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
9787  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
9788  * will be used to make the request.
9789  */
9790 Roo.data.HttpProxy = function(conn){
9791     Roo.data.HttpProxy.superclass.constructor.call(this);
9792     // is conn a conn config or a real conn?
9793     this.conn = conn;
9794     this.useAjax = !conn || !conn.events;
9795   
9796 };
9797
9798 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
9799     // thse are take from connection...
9800     
9801     /**
9802      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
9803      */
9804     /**
9805      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
9806      * extra parameters to each request made by this object. (defaults to undefined)
9807      */
9808     /**
9809      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
9810      *  to each request made by this object. (defaults to undefined)
9811      */
9812     /**
9813      * @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)
9814      */
9815     /**
9816      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
9817      */
9818      /**
9819      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
9820      * @type Boolean
9821      */
9822   
9823
9824     /**
9825      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
9826      * @type Boolean
9827      */
9828     /**
9829      * Return the {@link Roo.data.Connection} object being used by this Proxy.
9830      * @return {Connection} The Connection object. This object may be used to subscribe to events on
9831      * a finer-grained basis than the DataProxy events.
9832      */
9833     getConnection : function(){
9834         return this.useAjax ? Roo.Ajax : this.conn;
9835     },
9836
9837     /**
9838      * Load data from the configured {@link Roo.data.Connection}, read the data object into
9839      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
9840      * process that block using the passed callback.
9841      * @param {Object} params An object containing properties which are to be used as HTTP parameters
9842      * for the request to the remote server.
9843      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9844      * object into a block of Roo.data.Records.
9845      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9846      * The function must be passed <ul>
9847      * <li>The Record block object</li>
9848      * <li>The "arg" argument from the load function</li>
9849      * <li>A boolean success indicator</li>
9850      * </ul>
9851      * @param {Object} scope The scope in which to call the callback
9852      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9853      */
9854     load : function(params, reader, callback, scope, arg){
9855         if(this.fireEvent("beforeload", this, params) !== false){
9856             var  o = {
9857                 params : params || {},
9858                 request: {
9859                     callback : callback,
9860                     scope : scope,
9861                     arg : arg
9862                 },
9863                 reader: reader,
9864                 callback : this.loadResponse,
9865                 scope: this
9866             };
9867             if(this.useAjax){
9868                 Roo.applyIf(o, this.conn);
9869                 if(this.activeRequest){
9870                     Roo.Ajax.abort(this.activeRequest);
9871                 }
9872                 this.activeRequest = Roo.Ajax.request(o);
9873             }else{
9874                 this.conn.request(o);
9875             }
9876         }else{
9877             callback.call(scope||this, null, arg, false);
9878         }
9879     },
9880
9881     // private
9882     loadResponse : function(o, success, response){
9883         delete this.activeRequest;
9884         if(!success){
9885             this.fireEvent("loadexception", this, o, response);
9886             o.request.callback.call(o.request.scope, null, o.request.arg, false);
9887             return;
9888         }
9889         var result;
9890         try {
9891             result = o.reader.read(response);
9892         }catch(e){
9893             this.fireEvent("loadexception", this, o, response, e);
9894             o.request.callback.call(o.request.scope, null, o.request.arg, false);
9895             return;
9896         }
9897         
9898         this.fireEvent("load", this, o, o.request.arg);
9899         o.request.callback.call(o.request.scope, result, o.request.arg, true);
9900     },
9901
9902     // private
9903     update : function(dataSet){
9904
9905     },
9906
9907     // private
9908     updateResponse : function(dataSet){
9909
9910     }
9911 });/*
9912  * Based on:
9913  * Ext JS Library 1.1.1
9914  * Copyright(c) 2006-2007, Ext JS, LLC.
9915  *
9916  * Originally Released Under LGPL - original licence link has changed is not relivant.
9917  *
9918  * Fork - LGPL
9919  * <script type="text/javascript">
9920  */
9921
9922 /**
9923  * @class Roo.data.ScriptTagProxy
9924  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
9925  * other than the originating domain of the running page.<br><br>
9926  * <p>
9927  * <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
9928  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
9929  * <p>
9930  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
9931  * source code that is used as the source inside a &lt;script> tag.<br><br>
9932  * <p>
9933  * In order for the browser to process the returned data, the server must wrap the data object
9934  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
9935  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
9936  * depending on whether the callback name was passed:
9937  * <p>
9938  * <pre><code>
9939 boolean scriptTag = false;
9940 String cb = request.getParameter("callback");
9941 if (cb != null) {
9942     scriptTag = true;
9943     response.setContentType("text/javascript");
9944 } else {
9945     response.setContentType("application/x-json");
9946 }
9947 Writer out = response.getWriter();
9948 if (scriptTag) {
9949     out.write(cb + "(");
9950 }
9951 out.print(dataBlock.toJsonString());
9952 if (scriptTag) {
9953     out.write(");");
9954 }
9955 </pre></code>
9956  *
9957  * @constructor
9958  * @param {Object} config A configuration object.
9959  */
9960 Roo.data.ScriptTagProxy = function(config){
9961     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
9962     Roo.apply(this, config);
9963     this.head = document.getElementsByTagName("head")[0];
9964 };
9965
9966 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
9967
9968 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
9969     /**
9970      * @cfg {String} url The URL from which to request the data object.
9971      */
9972     /**
9973      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
9974      */
9975     timeout : 30000,
9976     /**
9977      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
9978      * the server the name of the callback function set up by the load call to process the returned data object.
9979      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
9980      * javascript output which calls this named function passing the data object as its only parameter.
9981      */
9982     callbackParam : "callback",
9983     /**
9984      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
9985      * name to the request.
9986      */
9987     nocache : true,
9988
9989     /**
9990      * Load data from the configured URL, read the data object into
9991      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9992      * process that block using the passed callback.
9993      * @param {Object} params An object containing properties which are to be used as HTTP parameters
9994      * for the request to the remote server.
9995      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9996      * object into a block of Roo.data.Records.
9997      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9998      * The function must be passed <ul>
9999      * <li>The Record block object</li>
10000      * <li>The "arg" argument from the load function</li>
10001      * <li>A boolean success indicator</li>
10002      * </ul>
10003      * @param {Object} scope The scope in which to call the callback
10004      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10005      */
10006     load : function(params, reader, callback, scope, arg){
10007         if(this.fireEvent("beforeload", this, params) !== false){
10008
10009             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
10010
10011             var url = this.url;
10012             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
10013             if(this.nocache){
10014                 url += "&_dc=" + (new Date().getTime());
10015             }
10016             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
10017             var trans = {
10018                 id : transId,
10019                 cb : "stcCallback"+transId,
10020                 scriptId : "stcScript"+transId,
10021                 params : params,
10022                 arg : arg,
10023                 url : url,
10024                 callback : callback,
10025                 scope : scope,
10026                 reader : reader
10027             };
10028             var conn = this;
10029
10030             window[trans.cb] = function(o){
10031                 conn.handleResponse(o, trans);
10032             };
10033
10034             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
10035
10036             if(this.autoAbort !== false){
10037                 this.abort();
10038             }
10039
10040             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
10041
10042             var script = document.createElement("script");
10043             script.setAttribute("src", url);
10044             script.setAttribute("type", "text/javascript");
10045             script.setAttribute("id", trans.scriptId);
10046             this.head.appendChild(script);
10047
10048             this.trans = trans;
10049         }else{
10050             callback.call(scope||this, null, arg, false);
10051         }
10052     },
10053
10054     // private
10055     isLoading : function(){
10056         return this.trans ? true : false;
10057     },
10058
10059     /**
10060      * Abort the current server request.
10061      */
10062     abort : function(){
10063         if(this.isLoading()){
10064             this.destroyTrans(this.trans);
10065         }
10066     },
10067
10068     // private
10069     destroyTrans : function(trans, isLoaded){
10070         this.head.removeChild(document.getElementById(trans.scriptId));
10071         clearTimeout(trans.timeoutId);
10072         if(isLoaded){
10073             window[trans.cb] = undefined;
10074             try{
10075                 delete window[trans.cb];
10076             }catch(e){}
10077         }else{
10078             // if hasn't been loaded, wait for load to remove it to prevent script error
10079             window[trans.cb] = function(){
10080                 window[trans.cb] = undefined;
10081                 try{
10082                     delete window[trans.cb];
10083                 }catch(e){}
10084             };
10085         }
10086     },
10087
10088     // private
10089     handleResponse : function(o, trans){
10090         this.trans = false;
10091         this.destroyTrans(trans, true);
10092         var result;
10093         try {
10094             result = trans.reader.readRecords(o);
10095         }catch(e){
10096             this.fireEvent("loadexception", this, o, trans.arg, e);
10097             trans.callback.call(trans.scope||window, null, trans.arg, false);
10098             return;
10099         }
10100         this.fireEvent("load", this, o, trans.arg);
10101         trans.callback.call(trans.scope||window, result, trans.arg, true);
10102     },
10103
10104     // private
10105     handleFailure : function(trans){
10106         this.trans = false;
10107         this.destroyTrans(trans, false);
10108         this.fireEvent("loadexception", this, null, trans.arg);
10109         trans.callback.call(trans.scope||window, null, trans.arg, false);
10110     }
10111 });/*
10112  * Based on:
10113  * Ext JS Library 1.1.1
10114  * Copyright(c) 2006-2007, Ext JS, LLC.
10115  *
10116  * Originally Released Under LGPL - original licence link has changed is not relivant.
10117  *
10118  * Fork - LGPL
10119  * <script type="text/javascript">
10120  */
10121
10122 /**
10123  * @class Roo.data.JsonReader
10124  * @extends Roo.data.DataReader
10125  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
10126  * based on mappings in a provided Roo.data.Record constructor.
10127  * 
10128  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
10129  * in the reply previously. 
10130  * 
10131  * <p>
10132  * Example code:
10133  * <pre><code>
10134 var RecordDef = Roo.data.Record.create([
10135     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
10136     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
10137 ]);
10138 var myReader = new Roo.data.JsonReader({
10139     totalProperty: "results",    // The property which contains the total dataset size (optional)
10140     root: "rows",                // The property which contains an Array of row objects
10141     id: "id"                     // The property within each row object that provides an ID for the record (optional)
10142 }, RecordDef);
10143 </code></pre>
10144  * <p>
10145  * This would consume a JSON file like this:
10146  * <pre><code>
10147 { 'results': 2, 'rows': [
10148     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
10149     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
10150 }
10151 </code></pre>
10152  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
10153  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
10154  * paged from the remote server.
10155  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
10156  * @cfg {String} root name of the property which contains the Array of row objects.
10157  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
10158  * @constructor
10159  * Create a new JsonReader
10160  * @param {Object} meta Metadata configuration options
10161  * @param {Object} recordType Either an Array of field definition objects,
10162  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
10163  */
10164 Roo.data.JsonReader = function(meta, recordType){
10165     
10166     meta = meta || {};
10167     // set some defaults:
10168     Roo.applyIf(meta, {
10169         totalProperty: 'total',
10170         successProperty : 'success',
10171         root : 'data',
10172         id : 'id'
10173     });
10174     
10175     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
10176 };
10177 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
10178     
10179     /**
10180      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
10181      * Used by Store query builder to append _requestMeta to params.
10182      * 
10183      */
10184     metaFromRemote : false,
10185     /**
10186      * This method is only used by a DataProxy which has retrieved data from a remote server.
10187      * @param {Object} response The XHR object which contains the JSON data in its responseText.
10188      * @return {Object} data A data block which is used by an Roo.data.Store object as
10189      * a cache of Roo.data.Records.
10190      */
10191     read : function(response){
10192         var json = response.responseText;
10193        
10194         var o = /* eval:var:o */ eval("("+json+")");
10195         if(!o) {
10196             throw {message: "JsonReader.read: Json object not found"};
10197         }
10198         
10199         if(o.metaData){
10200             
10201             delete this.ef;
10202             this.metaFromRemote = true;
10203             this.meta = o.metaData;
10204             this.recordType = Roo.data.Record.create(o.metaData.fields);
10205             this.onMetaChange(this.meta, this.recordType, o);
10206         }
10207         return this.readRecords(o);
10208     },
10209
10210     // private function a store will implement
10211     onMetaChange : function(meta, recordType, o){
10212
10213     },
10214
10215     /**
10216          * @ignore
10217          */
10218     simpleAccess: function(obj, subsc) {
10219         return obj[subsc];
10220     },
10221
10222         /**
10223          * @ignore
10224          */
10225     getJsonAccessor: function(){
10226         var re = /[\[\.]/;
10227         return function(expr) {
10228             try {
10229                 return(re.test(expr))
10230                     ? new Function("obj", "return obj." + expr)
10231                     : function(obj){
10232                         return obj[expr];
10233                     };
10234             } catch(e){}
10235             return Roo.emptyFn;
10236         };
10237     }(),
10238
10239     /**
10240      * Create a data block containing Roo.data.Records from an XML document.
10241      * @param {Object} o An object which contains an Array of row objects in the property specified
10242      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
10243      * which contains the total size of the dataset.
10244      * @return {Object} data A data block which is used by an Roo.data.Store object as
10245      * a cache of Roo.data.Records.
10246      */
10247     readRecords : function(o){
10248         /**
10249          * After any data loads, the raw JSON data is available for further custom processing.
10250          * @type Object
10251          */
10252         this.o = o;
10253         var s = this.meta, Record = this.recordType,
10254             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
10255
10256 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
10257         if (!this.ef) {
10258             if(s.totalProperty) {
10259                     this.getTotal = this.getJsonAccessor(s.totalProperty);
10260                 }
10261                 if(s.successProperty) {
10262                     this.getSuccess = this.getJsonAccessor(s.successProperty);
10263                 }
10264                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
10265                 if (s.id) {
10266                         var g = this.getJsonAccessor(s.id);
10267                         this.getId = function(rec) {
10268                                 var r = g(rec);  
10269                                 return (r === undefined || r === "") ? null : r;
10270                         };
10271                 } else {
10272                         this.getId = function(){return null;};
10273                 }
10274             this.ef = [];
10275             for(var jj = 0; jj < fl; jj++){
10276                 f = fi[jj];
10277                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
10278                 this.ef[jj] = this.getJsonAccessor(map);
10279             }
10280         }
10281
10282         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
10283         if(s.totalProperty){
10284             var vt = parseInt(this.getTotal(o), 10);
10285             if(!isNaN(vt)){
10286                 totalRecords = vt;
10287             }
10288         }
10289         if(s.successProperty){
10290             var vs = this.getSuccess(o);
10291             if(vs === false || vs === 'false'){
10292                 success = false;
10293             }
10294         }
10295         var records = [];
10296         for(var i = 0; i < c; i++){
10297                 var n = root[i];
10298             var values = {};
10299             var id = this.getId(n);
10300             for(var j = 0; j < fl; j++){
10301                 f = fi[j];
10302             var v = this.ef[j](n);
10303             if (!f.convert) {
10304                 Roo.log('missing convert for ' + f.name);
10305                 Roo.log(f);
10306                 continue;
10307             }
10308             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
10309             }
10310             var record = new Record(values, id);
10311             record.json = n;
10312             records[i] = record;
10313         }
10314         return {
10315             raw : o,
10316             success : success,
10317             records : records,
10318             totalRecords : totalRecords
10319         };
10320     }
10321 });/*
10322  * Based on:
10323  * Ext JS Library 1.1.1
10324  * Copyright(c) 2006-2007, Ext JS, LLC.
10325  *
10326  * Originally Released Under LGPL - original licence link has changed is not relivant.
10327  *
10328  * Fork - LGPL
10329  * <script type="text/javascript">
10330  */
10331
10332 /**
10333  * @class Roo.data.ArrayReader
10334  * @extends Roo.data.DataReader
10335  * Data reader class to create an Array of Roo.data.Record objects from an Array.
10336  * Each element of that Array represents a row of data fields. The
10337  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
10338  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
10339  * <p>
10340  * Example code:.
10341  * <pre><code>
10342 var RecordDef = Roo.data.Record.create([
10343     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
10344     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
10345 ]);
10346 var myReader = new Roo.data.ArrayReader({
10347     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
10348 }, RecordDef);
10349 </code></pre>
10350  * <p>
10351  * This would consume an Array like this:
10352  * <pre><code>
10353 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
10354   </code></pre>
10355  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
10356  * @constructor
10357  * Create a new JsonReader
10358  * @param {Object} meta Metadata configuration options.
10359  * @param {Object} recordType Either an Array of field definition objects
10360  * as specified to {@link Roo.data.Record#create},
10361  * or an {@link Roo.data.Record} object
10362  * created using {@link Roo.data.Record#create}.
10363  */
10364 Roo.data.ArrayReader = function(meta, recordType){
10365     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
10366 };
10367
10368 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
10369     /**
10370      * Create a data block containing Roo.data.Records from an XML document.
10371      * @param {Object} o An Array of row objects which represents the dataset.
10372      * @return {Object} data A data block which is used by an Roo.data.Store object as
10373      * a cache of Roo.data.Records.
10374      */
10375     readRecords : function(o){
10376         var sid = this.meta ? this.meta.id : null;
10377         var recordType = this.recordType, fields = recordType.prototype.fields;
10378         var records = [];
10379         var root = o;
10380             for(var i = 0; i < root.length; i++){
10381                     var n = root[i];
10382                 var values = {};
10383                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
10384                 for(var j = 0, jlen = fields.length; j < jlen; j++){
10385                 var f = fields.items[j];
10386                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
10387                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
10388                 v = f.convert(v);
10389                 values[f.name] = v;
10390             }
10391                 var record = new recordType(values, id);
10392                 record.json = n;
10393                 records[records.length] = record;
10394             }
10395             return {
10396                 records : records,
10397                 totalRecords : records.length
10398             };
10399     }
10400 });/*
10401  * - LGPL
10402  * * 
10403  */
10404
10405 /**
10406  * @class Roo.bootstrap.ComboBox
10407  * @extends Roo.bootstrap.TriggerField
10408  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
10409  * @cfg {Boolean} append (true|false) default false
10410  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
10411  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
10412  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
10413  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
10414  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
10415  * @constructor
10416  * Create a new ComboBox.
10417  * @param {Object} config Configuration options
10418  */
10419 Roo.bootstrap.ComboBox = function(config){
10420     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
10421     this.addEvents({
10422         /**
10423          * @event expand
10424          * Fires when the dropdown list is expanded
10425              * @param {Roo.bootstrap.ComboBox} combo This combo box
10426              */
10427         'expand' : true,
10428         /**
10429          * @event collapse
10430          * Fires when the dropdown list is collapsed
10431              * @param {Roo.bootstrap.ComboBox} combo This combo box
10432              */
10433         'collapse' : true,
10434         /**
10435          * @event beforeselect
10436          * Fires before a list item is selected. Return false to cancel the selection.
10437              * @param {Roo.bootstrap.ComboBox} combo This combo box
10438              * @param {Roo.data.Record} record The data record returned from the underlying store
10439              * @param {Number} index The index of the selected item in the dropdown list
10440              */
10441         'beforeselect' : true,
10442         /**
10443          * @event select
10444          * Fires when a list item is selected
10445              * @param {Roo.bootstrap.ComboBox} combo This combo box
10446              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
10447              * @param {Number} index The index of the selected item in the dropdown list
10448              */
10449         'select' : true,
10450         /**
10451          * @event beforequery
10452          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
10453          * The event object passed has these properties:
10454              * @param {Roo.bootstrap.ComboBox} combo This combo box
10455              * @param {String} query The query
10456              * @param {Boolean} forceAll true to force "all" query
10457              * @param {Boolean} cancel true to cancel the query
10458              * @param {Object} e The query event object
10459              */
10460         'beforequery': true,
10461          /**
10462          * @event add
10463          * Fires when the 'add' icon is pressed (add a listener to enable add button)
10464              * @param {Roo.bootstrap.ComboBox} combo This combo box
10465              */
10466         'add' : true,
10467         /**
10468          * @event edit
10469          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
10470              * @param {Roo.bootstrap.ComboBox} combo This combo box
10471              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
10472              */
10473         'edit' : true,
10474         /**
10475          * @event remove
10476          * Fires when the remove value from the combobox array
10477              * @param {Roo.bootstrap.ComboBox} combo This combo box
10478              */
10479         'remove' : true
10480         
10481     });
10482     
10483     this.item = [];
10484     this.tickItems = [];
10485     
10486     this.selectedIndex = -1;
10487     if(this.mode == 'local'){
10488         if(config.queryDelay === undefined){
10489             this.queryDelay = 10;
10490         }
10491         if(config.minChars === undefined){
10492             this.minChars = 0;
10493         }
10494     }
10495 };
10496
10497 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
10498      
10499     /**
10500      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
10501      * rendering into an Roo.Editor, defaults to false)
10502      */
10503     /**
10504      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
10505      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
10506      */
10507     /**
10508      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
10509      */
10510     /**
10511      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
10512      * the dropdown list (defaults to undefined, with no header element)
10513      */
10514
10515      /**
10516      * @cfg {String/Roo.Template} tpl The template to use to render the output
10517      */
10518      
10519      /**
10520      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
10521      */
10522     listWidth: undefined,
10523     /**
10524      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
10525      * mode = 'remote' or 'text' if mode = 'local')
10526      */
10527     displayField: undefined,
10528     /**
10529      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
10530      * mode = 'remote' or 'value' if mode = 'local'). 
10531      * Note: use of a valueField requires the user make a selection
10532      * in order for a value to be mapped.
10533      */
10534     valueField: undefined,
10535     
10536     
10537     /**
10538      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
10539      * field's data value (defaults to the underlying DOM element's name)
10540      */
10541     hiddenName: undefined,
10542     /**
10543      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
10544      */
10545     listClass: '',
10546     /**
10547      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
10548      */
10549     selectedClass: 'active',
10550     
10551     /**
10552      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
10553      */
10554     shadow:'sides',
10555     /**
10556      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
10557      * anchor positions (defaults to 'tl-bl')
10558      */
10559     listAlign: 'tl-bl?',
10560     /**
10561      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
10562      */
10563     maxHeight: 300,
10564     /**
10565      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
10566      * query specified by the allQuery config option (defaults to 'query')
10567      */
10568     triggerAction: 'query',
10569     /**
10570      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
10571      * (defaults to 4, does not apply if editable = false)
10572      */
10573     minChars : 4,
10574     /**
10575      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
10576      * delay (typeAheadDelay) if it matches a known value (defaults to false)
10577      */
10578     typeAhead: false,
10579     /**
10580      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
10581      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
10582      */
10583     queryDelay: 500,
10584     /**
10585      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
10586      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
10587      */
10588     pageSize: 0,
10589     /**
10590      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
10591      * when editable = true (defaults to false)
10592      */
10593     selectOnFocus:false,
10594     /**
10595      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
10596      */
10597     queryParam: 'query',
10598     /**
10599      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
10600      * when mode = 'remote' (defaults to 'Loading...')
10601      */
10602     loadingText: 'Loading...',
10603     /**
10604      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
10605      */
10606     resizable: false,
10607     /**
10608      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
10609      */
10610     handleHeight : 8,
10611     /**
10612      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
10613      * traditional select (defaults to true)
10614      */
10615     editable: true,
10616     /**
10617      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
10618      */
10619     allQuery: '',
10620     /**
10621      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
10622      */
10623     mode: 'remote',
10624     /**
10625      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
10626      * listWidth has a higher value)
10627      */
10628     minListWidth : 70,
10629     /**
10630      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
10631      * allow the user to set arbitrary text into the field (defaults to false)
10632      */
10633     forceSelection:false,
10634     /**
10635      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
10636      * if typeAhead = true (defaults to 250)
10637      */
10638     typeAheadDelay : 250,
10639     /**
10640      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
10641      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
10642      */
10643     valueNotFoundText : undefined,
10644     /**
10645      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
10646      */
10647     blockFocus : false,
10648     
10649     /**
10650      * @cfg {Boolean} disableClear Disable showing of clear button.
10651      */
10652     disableClear : false,
10653     /**
10654      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
10655      */
10656     alwaysQuery : false,
10657     
10658     /**
10659      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
10660      */
10661     multiple : false,
10662     
10663     //private
10664     addicon : false,
10665     editicon: false,
10666     
10667     page: 0,
10668     hasQuery: false,
10669     append: false,
10670     loadNext: false,
10671     autoFocus : true,
10672     tickable : false,
10673     btnPosition : 'right',
10674     triggerList : true,
10675     showToggleBtn : true,
10676     // element that contains real text value.. (when hidden is used..)
10677     
10678     getAutoCreate : function()
10679     {
10680         var cfg = false;
10681         
10682         /*
10683          *  Normal ComboBox
10684          */
10685         if(!this.tickable){
10686             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
10687             return cfg;
10688         }
10689         
10690         /*
10691          *  ComboBox with tickable selections
10692          */
10693              
10694         var align = this.labelAlign || this.parentLabelAlign();
10695         
10696         cfg = {
10697             cls : 'form-group roo-combobox-tickable' //input-group
10698         };
10699         
10700         
10701         var buttons = {
10702             tag : 'div',
10703             cls : 'tickable-buttons',
10704             cn : [
10705                 {
10706                     tag : 'button',
10707                     type : 'button',
10708                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
10709                     html : 'Edit'
10710                 },
10711                 {
10712                     tag : 'button',
10713                     type : 'button',
10714                     name : 'ok',
10715                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
10716                     html : 'Done'
10717                 },
10718                 {
10719                     tag : 'button',
10720                     type : 'button',
10721                     name : 'cancel',
10722                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
10723                     html : 'Cancel'
10724                 }
10725             ]
10726         };
10727         
10728         var _this = this;
10729         Roo.each(buttons.cn, function(c){
10730             if (_this.size) {
10731                 c.cls += ' btn-' + _this.size;
10732             }
10733
10734             if (_this.disabled) {
10735                 c.disabled = true;
10736             }
10737         });
10738         
10739         var box = {
10740             tag: 'div',
10741             cn: [
10742                 {
10743                     tag: 'input',
10744                     type : 'hidden',
10745                     cls: 'form-hidden-field'
10746                 },
10747                 {
10748                     tag: 'ul',
10749                     cls: 'select2-choices',
10750                     cn:[
10751                         {
10752                             tag: 'li',
10753                             cls: 'select2-search-field',
10754                             cn: [
10755
10756                                 buttons
10757                             ]
10758                         }
10759                     ]
10760                 }
10761             ]
10762         }
10763         
10764         var combobox = {
10765             cls: 'select2-container input-group select2-container-multi',
10766             cn: [
10767                 box
10768 //                {
10769 //                    tag: 'ul',
10770 //                    cls: 'typeahead typeahead-long dropdown-menu',
10771 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
10772 //                }
10773             ]
10774         };
10775         
10776         if (align ==='left' && this.fieldLabel.length) {
10777             
10778                 Roo.log("left and has label");
10779                 cfg.cn = [
10780                     
10781                     {
10782                         tag: 'label',
10783                         'for' :  id,
10784                         cls : 'control-label col-sm-' + this.labelWidth,
10785                         html : this.fieldLabel
10786                         
10787                     },
10788                     {
10789                         cls : "col-sm-" + (12 - this.labelWidth), 
10790                         cn: [
10791                             combobox
10792                         ]
10793                     }
10794                     
10795                 ];
10796         } else if ( this.fieldLabel.length) {
10797                 Roo.log(" label");
10798                  cfg.cn = [
10799                    
10800                     {
10801                         tag: 'label',
10802                         //cls : 'input-group-addon',
10803                         html : this.fieldLabel
10804                         
10805                     },
10806                     
10807                     combobox
10808                     
10809                 ];
10810
10811         } else {
10812             
10813                 Roo.log(" no label && no align");
10814                 cfg = combobox
10815                      
10816                 
10817         }
10818          
10819         var settings=this;
10820         ['xs','sm','md','lg'].map(function(size){
10821             if (settings[size]) {
10822                 cfg.cls += ' col-' + size + '-' + settings[size];
10823             }
10824         });
10825         
10826         return cfg;
10827         
10828     },
10829     
10830     // private
10831     initEvents: function()
10832     {
10833         
10834         if (!this.store) {
10835             throw "can not find store for combo";
10836         }
10837         this.store = Roo.factory(this.store, Roo.data);
10838         
10839         if(this.tickable){
10840             this.initTickableEvents();
10841             return;
10842         }
10843         
10844         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
10845         
10846         if(this.hiddenName){
10847             
10848             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10849             
10850             this.hiddenField.dom.value =
10851                 this.hiddenValue !== undefined ? this.hiddenValue :
10852                 this.value !== undefined ? this.value : '';
10853
10854             // prevent input submission
10855             this.el.dom.removeAttribute('name');
10856             this.hiddenField.dom.setAttribute('name', this.hiddenName);
10857              
10858              
10859         }
10860         //if(Roo.isGecko){
10861         //    this.el.dom.setAttribute('autocomplete', 'off');
10862         //}
10863         
10864         var cls = 'x-combo-list';
10865         
10866         //this.list = new Roo.Layer({
10867         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
10868         //});
10869         
10870         var _this = this;
10871         
10872         (function(){
10873             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
10874             _this.list.setWidth(lw);
10875         }).defer(100);
10876         
10877         this.list.on('mouseover', this.onViewOver, this);
10878         this.list.on('mousemove', this.onViewMove, this);
10879         
10880         this.list.on('scroll', this.onViewScroll, this);
10881         
10882         /*
10883         this.list.swallowEvent('mousewheel');
10884         this.assetHeight = 0;
10885
10886         if(this.title){
10887             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
10888             this.assetHeight += this.header.getHeight();
10889         }
10890
10891         this.innerList = this.list.createChild({cls:cls+'-inner'});
10892         this.innerList.on('mouseover', this.onViewOver, this);
10893         this.innerList.on('mousemove', this.onViewMove, this);
10894         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
10895         
10896         if(this.allowBlank && !this.pageSize && !this.disableClear){
10897             this.footer = this.list.createChild({cls:cls+'-ft'});
10898             this.pageTb = new Roo.Toolbar(this.footer);
10899            
10900         }
10901         if(this.pageSize){
10902             this.footer = this.list.createChild({cls:cls+'-ft'});
10903             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
10904                     {pageSize: this.pageSize});
10905             
10906         }
10907         
10908         if (this.pageTb && this.allowBlank && !this.disableClear) {
10909             var _this = this;
10910             this.pageTb.add(new Roo.Toolbar.Fill(), {
10911                 cls: 'x-btn-icon x-btn-clear',
10912                 text: '&#160;',
10913                 handler: function()
10914                 {
10915                     _this.collapse();
10916                     _this.clearValue();
10917                     _this.onSelect(false, -1);
10918                 }
10919             });
10920         }
10921         if (this.footer) {
10922             this.assetHeight += this.footer.getHeight();
10923         }
10924         */
10925             
10926         if(!this.tpl){
10927             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
10928         }
10929
10930         this.view = new Roo.View(this.list, this.tpl, {
10931             singleSelect:true, store: this.store, selectedClass: this.selectedClass
10932         });
10933         //this.view.wrapEl.setDisplayed(false);
10934         this.view.on('click', this.onViewClick, this);
10935         
10936         
10937         
10938         this.store.on('beforeload', this.onBeforeLoad, this);
10939         this.store.on('load', this.onLoad, this);
10940         this.store.on('loadexception', this.onLoadException, this);
10941         /*
10942         if(this.resizable){
10943             this.resizer = new Roo.Resizable(this.list,  {
10944                pinned:true, handles:'se'
10945             });
10946             this.resizer.on('resize', function(r, w, h){
10947                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
10948                 this.listWidth = w;
10949                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
10950                 this.restrictHeight();
10951             }, this);
10952             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
10953         }
10954         */
10955         if(!this.editable){
10956             this.editable = true;
10957             this.setEditable(false);
10958         }
10959         
10960         /*
10961         
10962         if (typeof(this.events.add.listeners) != 'undefined') {
10963             
10964             this.addicon = this.wrap.createChild(
10965                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
10966        
10967             this.addicon.on('click', function(e) {
10968                 this.fireEvent('add', this);
10969             }, this);
10970         }
10971         if (typeof(this.events.edit.listeners) != 'undefined') {
10972             
10973             this.editicon = this.wrap.createChild(
10974                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
10975             if (this.addicon) {
10976                 this.editicon.setStyle('margin-left', '40px');
10977             }
10978             this.editicon.on('click', function(e) {
10979                 
10980                 // we fire even  if inothing is selected..
10981                 this.fireEvent('edit', this, this.lastData );
10982                 
10983             }, this);
10984         }
10985         */
10986         
10987         this.keyNav = new Roo.KeyNav(this.inputEl(), {
10988             "up" : function(e){
10989                 this.inKeyMode = true;
10990                 this.selectPrev();
10991             },
10992
10993             "down" : function(e){
10994                 if(!this.isExpanded()){
10995                     this.onTriggerClick();
10996                 }else{
10997                     this.inKeyMode = true;
10998                     this.selectNext();
10999                 }
11000             },
11001
11002             "enter" : function(e){
11003 //                this.onViewClick();
11004                 //return true;
11005                 this.collapse();
11006                 
11007                 if(this.fireEvent("specialkey", this, e)){
11008                     this.onViewClick(false);
11009                 }
11010                 
11011                 return true;
11012             },
11013
11014             "esc" : function(e){
11015                 this.collapse();
11016             },
11017
11018             "tab" : function(e){
11019                 this.collapse();
11020                 
11021                 if(this.fireEvent("specialkey", this, e)){
11022                     this.onViewClick(false);
11023                 }
11024                 
11025                 return true;
11026             },
11027
11028             scope : this,
11029
11030             doRelay : function(foo, bar, hname){
11031                 if(hname == 'down' || this.scope.isExpanded()){
11032                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11033                 }
11034                 return true;
11035             },
11036
11037             forceKeyDown: true
11038         });
11039         
11040         
11041         this.queryDelay = Math.max(this.queryDelay || 10,
11042                 this.mode == 'local' ? 10 : 250);
11043         
11044         
11045         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11046         
11047         if(this.typeAhead){
11048             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11049         }
11050         if(this.editable !== false){
11051             this.inputEl().on("keyup", this.onKeyUp, this);
11052         }
11053         if(this.forceSelection){
11054             this.inputEl().on('blur', this.doForce, this);
11055         }
11056         
11057         if(this.multiple){
11058             this.choices = this.el.select('ul.select2-choices', true).first();
11059             this.searchField = this.el.select('ul li.select2-search-field', true).first();
11060         }
11061     },
11062     
11063     initTickableEvents: function()
11064     {   
11065         this.createList();
11066         
11067         if(this.hiddenName){
11068             
11069             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11070             
11071             this.hiddenField.dom.value =
11072                 this.hiddenValue !== undefined ? this.hiddenValue :
11073                 this.value !== undefined ? this.value : '';
11074
11075             // prevent input submission
11076             this.el.dom.removeAttribute('name');
11077             this.hiddenField.dom.setAttribute('name', this.hiddenName);
11078              
11079              
11080         }
11081         
11082 //        this.list = this.el.select('ul.dropdown-menu',true).first();
11083         
11084         this.choices = this.el.select('ul.select2-choices', true).first();
11085         this.searchField = this.el.select('ul li.select2-search-field', true).first();
11086         if(this.triggerList){
11087             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
11088         }
11089          
11090         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
11091         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
11092         
11093         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
11094         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
11095         
11096         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
11097         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
11098         
11099         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
11100         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
11101         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
11102         
11103         this.okBtn.hide();
11104         this.cancelBtn.hide();
11105         
11106         var _this = this;
11107         
11108         (function(){
11109             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11110             _this.list.setWidth(lw);
11111         }).defer(100);
11112         
11113         this.list.on('mouseover', this.onViewOver, this);
11114         this.list.on('mousemove', this.onViewMove, this);
11115         
11116         this.list.on('scroll', this.onViewScroll, this);
11117         
11118         if(!this.tpl){
11119             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>';
11120         }
11121
11122         this.view = new Roo.View(this.list, this.tpl, {
11123             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
11124         });
11125         
11126         //this.view.wrapEl.setDisplayed(false);
11127         this.view.on('click', this.onViewClick, this);
11128         
11129         
11130         
11131         this.store.on('beforeload', this.onBeforeLoad, this);
11132         this.store.on('load', this.onLoad, this);
11133         this.store.on('loadexception', this.onLoadException, this);
11134         
11135 //        this.keyNav = new Roo.KeyNav(this.inputEl(), {
11136 //            "up" : function(e){
11137 //                this.inKeyMode = true;
11138 //                this.selectPrev();
11139 //            },
11140 //
11141 //            "down" : function(e){
11142 //                if(!this.isExpanded()){
11143 //                    this.onTriggerClick();
11144 //                }else{
11145 //                    this.inKeyMode = true;
11146 //                    this.selectNext();
11147 //                }
11148 //            },
11149 //
11150 //            "enter" : function(e){
11151 ////                this.onViewClick();
11152 //                //return true;
11153 //                this.collapse();
11154 //                
11155 //                if(this.fireEvent("specialkey", this, e)){
11156 //                    this.onViewClick(false);
11157 //                }
11158 //                
11159 //                return true;
11160 //            },
11161 //
11162 //            "esc" : function(e){
11163 //                this.collapse();
11164 //            },
11165 //
11166 //            "tab" : function(e){
11167 //                this.collapse();
11168 //                
11169 //                if(this.fireEvent("specialkey", this, e)){
11170 //                    this.onViewClick(false);
11171 //                }
11172 //                
11173 //                return true;
11174 //            },
11175 //
11176 //            scope : this,
11177 //
11178 //            doRelay : function(foo, bar, hname){
11179 //                if(hname == 'down' || this.scope.isExpanded()){
11180 //                   return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11181 //                }
11182 //                return true;
11183 //            },
11184 //
11185 //            forceKeyDown: true
11186 //        });
11187         
11188         
11189         this.queryDelay = Math.max(this.queryDelay || 10,
11190                 this.mode == 'local' ? 10 : 250);
11191         
11192         
11193         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11194         
11195         if(this.typeAhead){
11196             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11197         }
11198     },
11199
11200     onDestroy : function(){
11201         if(this.view){
11202             this.view.setStore(null);
11203             this.view.el.removeAllListeners();
11204             this.view.el.remove();
11205             this.view.purgeListeners();
11206         }
11207         if(this.list){
11208             this.list.dom.innerHTML  = '';
11209         }
11210         
11211         if(this.store){
11212             this.store.un('beforeload', this.onBeforeLoad, this);
11213             this.store.un('load', this.onLoad, this);
11214             this.store.un('loadexception', this.onLoadException, this);
11215         }
11216         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
11217     },
11218
11219     // private
11220     fireKey : function(e){
11221         if(e.isNavKeyPress() && !this.list.isVisible()){
11222             this.fireEvent("specialkey", this, e);
11223         }
11224     },
11225
11226     // private
11227     onResize: function(w, h){
11228 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
11229 //        
11230 //        if(typeof w != 'number'){
11231 //            // we do not handle it!?!?
11232 //            return;
11233 //        }
11234 //        var tw = this.trigger.getWidth();
11235 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
11236 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
11237 //        var x = w - tw;
11238 //        this.inputEl().setWidth( this.adjustWidth('input', x));
11239 //            
11240 //        //this.trigger.setStyle('left', x+'px');
11241 //        
11242 //        if(this.list && this.listWidth === undefined){
11243 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
11244 //            this.list.setWidth(lw);
11245 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11246 //        }
11247         
11248     
11249         
11250     },
11251
11252     /**
11253      * Allow or prevent the user from directly editing the field text.  If false is passed,
11254      * the user will only be able to select from the items defined in the dropdown list.  This method
11255      * is the runtime equivalent of setting the 'editable' config option at config time.
11256      * @param {Boolean} value True to allow the user to directly edit the field text
11257      */
11258     setEditable : function(value){
11259         if(value == this.editable){
11260             return;
11261         }
11262         this.editable = value;
11263         if(!value){
11264             this.inputEl().dom.setAttribute('readOnly', true);
11265             this.inputEl().on('mousedown', this.onTriggerClick,  this);
11266             this.inputEl().addClass('x-combo-noedit');
11267         }else{
11268             this.inputEl().dom.setAttribute('readOnly', false);
11269             this.inputEl().un('mousedown', this.onTriggerClick,  this);
11270             this.inputEl().removeClass('x-combo-noedit');
11271         }
11272     },
11273
11274     // private
11275     
11276     onBeforeLoad : function(combo,opts){
11277         if(!this.hasFocus){
11278             return;
11279         }
11280          if (!opts.add) {
11281             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
11282          }
11283         this.restrictHeight();
11284         this.selectedIndex = -1;
11285     },
11286
11287     // private
11288     onLoad : function(){
11289         
11290         this.hasQuery = false;
11291         
11292         if(!this.hasFocus){
11293             return;
11294         }
11295         
11296         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11297             this.loading.hide();
11298         }
11299         
11300         if(this.store.getCount() > 0){
11301             this.expand();
11302 //            this.restrictHeight();
11303             if(this.lastQuery == this.allQuery){
11304                 if(this.editable && !this.tickable){
11305                     this.inputEl().dom.select();
11306                 }
11307                 
11308                 if(
11309                     !this.selectByValue(this.value, true) &&
11310                     this.autoFocus && (typeof(this.store.lastOptions.add) == 'undefined' || 
11311                     this.store.lastOptions.add != true)
11312                 ){
11313                     this.select(0, true);
11314                 }
11315             }else{
11316                 if(this.autoFocus){
11317                     this.selectNext();
11318                 }
11319                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
11320                     this.taTask.delay(this.typeAheadDelay);
11321                 }
11322             }
11323         }else{
11324             this.onEmptyResults();
11325         }
11326         
11327         //this.el.focus();
11328     },
11329     // private
11330     onLoadException : function()
11331     {
11332         this.hasQuery = false;
11333         
11334         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11335             this.loading.hide();
11336         }
11337         
11338         this.collapse();
11339         Roo.log(this.store.reader.jsonData);
11340         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
11341             // fixme
11342             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
11343         }
11344         
11345         
11346     },
11347     // private
11348     onTypeAhead : function(){
11349         if(this.store.getCount() > 0){
11350             var r = this.store.getAt(0);
11351             var newValue = r.data[this.displayField];
11352             var len = newValue.length;
11353             var selStart = this.getRawValue().length;
11354             
11355             if(selStart != len){
11356                 this.setRawValue(newValue);
11357                 this.selectText(selStart, newValue.length);
11358             }
11359         }
11360     },
11361
11362     // private
11363     onSelect : function(record, index){
11364         
11365         if(this.fireEvent('beforeselect', this, record, index) !== false){
11366         
11367             this.setFromData(index > -1 ? record.data : false);
11368             
11369             this.collapse();
11370             this.fireEvent('select', this, record, index);
11371         }
11372     },
11373
11374     /**
11375      * Returns the currently selected field value or empty string if no value is set.
11376      * @return {String} value The selected value
11377      */
11378     getValue : function(){
11379         
11380         if(this.multiple){
11381             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
11382         }
11383         
11384         if(this.valueField){
11385             return typeof this.value != 'undefined' ? this.value : '';
11386         }else{
11387             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
11388         }
11389     },
11390
11391     /**
11392      * Clears any text/value currently set in the field
11393      */
11394     clearValue : function(){
11395         if(this.hiddenField){
11396             this.hiddenField.dom.value = '';
11397         }
11398         this.value = '';
11399         this.setRawValue('');
11400         this.lastSelectionText = '';
11401         
11402     },
11403
11404     /**
11405      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
11406      * will be displayed in the field.  If the value does not match the data value of an existing item,
11407      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
11408      * Otherwise the field will be blank (although the value will still be set).
11409      * @param {String} value The value to match
11410      */
11411     setValue : function(v){
11412         if(this.multiple){
11413             this.syncValue();
11414             return;
11415         }
11416         
11417         var text = v;
11418         if(this.valueField){
11419             var r = this.findRecord(this.valueField, v);
11420             if(r){
11421                 text = r.data[this.displayField];
11422             }else if(this.valueNotFoundText !== undefined){
11423                 text = this.valueNotFoundText;
11424             }
11425         }
11426         this.lastSelectionText = text;
11427         if(this.hiddenField){
11428             this.hiddenField.dom.value = v;
11429         }
11430         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
11431         this.value = v;
11432     },
11433     /**
11434      * @property {Object} the last set data for the element
11435      */
11436     
11437     lastData : false,
11438     /**
11439      * Sets the value of the field based on a object which is related to the record format for the store.
11440      * @param {Object} value the value to set as. or false on reset?
11441      */
11442     setFromData : function(o){
11443         
11444         if(this.multiple){
11445             this.addItem(o);
11446             return;
11447         }
11448             
11449         var dv = ''; // display value
11450         var vv = ''; // value value..
11451         this.lastData = o;
11452         if (this.displayField) {
11453             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11454         } else {
11455             // this is an error condition!!!
11456             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
11457         }
11458         
11459         if(this.valueField){
11460             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
11461         }
11462         
11463         if(this.hiddenField){
11464             this.hiddenField.dom.value = vv;
11465             
11466             this.lastSelectionText = dv;
11467             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11468             this.value = vv;
11469             return;
11470         }
11471         // no hidden field.. - we store the value in 'value', but still display
11472         // display field!!!!
11473         this.lastSelectionText = dv;
11474         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11475         this.value = vv;
11476         
11477         
11478     },
11479     // private
11480     reset : function(){
11481         // overridden so that last data is reset..
11482         this.setValue(this.originalValue);
11483         this.clearInvalid();
11484         this.lastData = false;
11485         if (this.view) {
11486             this.view.clearSelections();
11487         }
11488     },
11489     // private
11490     findRecord : function(prop, value){
11491         var record;
11492         if(this.store.getCount() > 0){
11493             this.store.each(function(r){
11494                 if(r.data[prop] == value){
11495                     record = r;
11496                     return false;
11497                 }
11498                 return true;
11499             });
11500         }
11501         return record;
11502     },
11503     
11504     getName: function()
11505     {
11506         // returns hidden if it's set..
11507         if (!this.rendered) {return ''};
11508         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
11509         
11510     },
11511     // private
11512     onViewMove : function(e, t){
11513         this.inKeyMode = false;
11514     },
11515
11516     // private
11517     onViewOver : function(e, t){
11518         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
11519             return;
11520         }
11521         var item = this.view.findItemFromChild(t);
11522         
11523         if(item){
11524             var index = this.view.indexOf(item);
11525             this.select(index, false);
11526         }
11527     },
11528
11529     // private
11530     onViewClick : function(view, doFocus, el, e)
11531     {
11532         var index = this.view.getSelectedIndexes()[0];
11533         
11534         var r = this.store.getAt(index);
11535         
11536         if(this.tickable){
11537             
11538             if(e.getTarget().nodeName.toLowerCase() != 'input'){
11539                 return;
11540             }
11541             
11542             var rm = false;
11543             var _this = this;
11544             
11545             Roo.each(this.tickItems, function(v,k){
11546                 
11547                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
11548                     _this.tickItems.splice(k, 1);
11549                     rm = true;
11550                     return;
11551                 }
11552             })
11553             
11554             if(rm){
11555                 return;
11556             }
11557             
11558             this.tickItems.push(r.data);
11559             return;
11560         }
11561         
11562         if(r){
11563             this.onSelect(r, index);
11564         }
11565         if(doFocus !== false && !this.blockFocus){
11566             this.inputEl().focus();
11567         }
11568     },
11569
11570     // private
11571     restrictHeight : function(){
11572         //this.innerList.dom.style.height = '';
11573         //var inner = this.innerList.dom;
11574         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
11575         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
11576         //this.list.beginUpdate();
11577         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
11578         this.list.alignTo(this.inputEl(), this.listAlign);
11579         this.list.alignTo(this.inputEl(), this.listAlign);
11580         //this.list.endUpdate();
11581     },
11582
11583     // private
11584     onEmptyResults : function(){
11585         this.collapse();
11586     },
11587
11588     /**
11589      * Returns true if the dropdown list is expanded, else false.
11590      */
11591     isExpanded : function(){
11592         return this.list.isVisible();
11593     },
11594
11595     /**
11596      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
11597      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11598      * @param {String} value The data value of the item to select
11599      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11600      * selected item if it is not currently in view (defaults to true)
11601      * @return {Boolean} True if the value matched an item in the list, else false
11602      */
11603     selectByValue : function(v, scrollIntoView){
11604         if(v !== undefined && v !== null){
11605             var r = this.findRecord(this.valueField || this.displayField, v);
11606             if(r){
11607                 this.select(this.store.indexOf(r), scrollIntoView);
11608                 return true;
11609             }
11610         }
11611         return false;
11612     },
11613
11614     /**
11615      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
11616      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11617      * @param {Number} index The zero-based index of the list item to select
11618      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11619      * selected item if it is not currently in view (defaults to true)
11620      */
11621     select : function(index, scrollIntoView){
11622         this.selectedIndex = index;
11623         this.view.select(index);
11624         if(scrollIntoView !== false){
11625             var el = this.view.getNode(index);
11626             if(el && !this.multiple && !this.tickable){
11627                 this.list.scrollChildIntoView(el, false);
11628             }
11629         }
11630     },
11631
11632     // private
11633     selectNext : function(){
11634         var ct = this.store.getCount();
11635         if(ct > 0){
11636             if(this.selectedIndex == -1){
11637                 this.select(0);
11638             }else if(this.selectedIndex < ct-1){
11639                 this.select(this.selectedIndex+1);
11640             }
11641         }
11642     },
11643
11644     // private
11645     selectPrev : function(){
11646         var ct = this.store.getCount();
11647         if(ct > 0){
11648             if(this.selectedIndex == -1){
11649                 this.select(0);
11650             }else if(this.selectedIndex != 0){
11651                 this.select(this.selectedIndex-1);
11652             }
11653         }
11654     },
11655
11656     // private
11657     onKeyUp : function(e){
11658         if(this.editable !== false && !e.isSpecialKey()){
11659             this.lastKey = e.getKey();
11660             this.dqTask.delay(this.queryDelay);
11661         }
11662     },
11663
11664     // private
11665     validateBlur : function(){
11666         return !this.list || !this.list.isVisible();   
11667     },
11668
11669     // private
11670     initQuery : function(){
11671         this.doQuery(this.getRawValue());
11672     },
11673
11674     // private
11675     doForce : function(){
11676         if(this.inputEl().dom.value.length > 0){
11677             this.inputEl().dom.value =
11678                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
11679              
11680         }
11681     },
11682
11683     /**
11684      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
11685      * query allowing the query action to be canceled if needed.
11686      * @param {String} query The SQL query to execute
11687      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
11688      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
11689      * saved in the current store (defaults to false)
11690      */
11691     doQuery : function(q, forceAll){
11692         
11693         if(q === undefined || q === null){
11694             q = '';
11695         }
11696         var qe = {
11697             query: q,
11698             forceAll: forceAll,
11699             combo: this,
11700             cancel:false
11701         };
11702         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
11703             return false;
11704         }
11705         q = qe.query;
11706         
11707         forceAll = qe.forceAll;
11708         if(forceAll === true || (q.length >= this.minChars)){
11709             
11710             this.hasQuery = true;
11711             
11712             if(this.lastQuery != q || this.alwaysQuery){
11713                 this.lastQuery = q;
11714                 if(this.mode == 'local'){
11715                     this.selectedIndex = -1;
11716                     if(forceAll){
11717                         this.store.clearFilter();
11718                     }else{
11719                         this.store.filter(this.displayField, q);
11720                     }
11721                     this.onLoad();
11722                 }else{
11723                     this.store.baseParams[this.queryParam] = q;
11724                     
11725                     var options = {params : this.getParams(q)};
11726                     
11727                     if(this.loadNext){
11728                         options.add = true;
11729                         options.params.start = this.page * this.pageSize;
11730                     }
11731                     
11732                     this.store.load(options);
11733                     /*
11734                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
11735                      *  we should expand the list on onLoad
11736                      *  so command out it
11737                      */
11738 //                    this.expand();
11739                 }
11740             }else{
11741                 this.selectedIndex = -1;
11742                 this.onLoad();   
11743             }
11744         }
11745         
11746         this.loadNext = false;
11747     },
11748
11749     // private
11750     getParams : function(q){
11751         var p = {};
11752         //p[this.queryParam] = q;
11753         
11754         if(this.pageSize){
11755             p.start = 0;
11756             p.limit = this.pageSize;
11757         }
11758         return p;
11759     },
11760
11761     /**
11762      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
11763      */
11764     collapse : function(){
11765         if(!this.isExpanded()){
11766             return;
11767         }
11768         
11769         this.list.hide();
11770         
11771         if(this.tickable){
11772             this.okBtn.hide();
11773             this.cancelBtn.hide();
11774             this.trigger.show();
11775         }
11776         
11777         Roo.get(document).un('mousedown', this.collapseIf, this);
11778         Roo.get(document).un('mousewheel', this.collapseIf, this);
11779         if (!this.editable) {
11780             Roo.get(document).un('keydown', this.listKeyPress, this);
11781         }
11782         this.fireEvent('collapse', this);
11783     },
11784
11785     // private
11786     collapseIf : function(e){
11787         var in_combo  = e.within(this.el);
11788         var in_list =  e.within(this.list);
11789         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
11790         
11791         if (in_combo || in_list || is_list) {
11792             //e.stopPropagation();
11793             return;
11794         }
11795         
11796         if(this.tickable){
11797             this.onTickableFooterButtonClick(e, false, false);
11798         }
11799
11800         this.collapse();
11801         
11802     },
11803
11804     /**
11805      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
11806      */
11807     expand : function(){
11808        
11809         if(this.isExpanded() || !this.hasFocus){
11810             return;
11811         }
11812         
11813         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
11814         this.list.setWidth(lw);
11815         
11816         
11817          Roo.log('expand');
11818         
11819         this.list.show();
11820         
11821         this.restrictHeight();
11822         
11823         if(this.tickable){
11824             
11825             this.tickItems = Roo.apply([], this.item);
11826             
11827             this.okBtn.show();
11828             this.cancelBtn.show();
11829             this.trigger.hide();
11830             
11831         }
11832         
11833         Roo.get(document).on('mousedown', this.collapseIf, this);
11834         Roo.get(document).on('mousewheel', this.collapseIf, this);
11835         if (!this.editable) {
11836             Roo.get(document).on('keydown', this.listKeyPress, this);
11837         }
11838         
11839         this.fireEvent('expand', this);
11840     },
11841
11842     // private
11843     // Implements the default empty TriggerField.onTriggerClick function
11844     onTriggerClick : function(e)
11845     {
11846         Roo.log('trigger click');
11847         
11848         if(this.disabled || !this.triggerList){
11849             return;
11850         }
11851         
11852         this.page = 0;
11853         this.loadNext = false;
11854         
11855         if(this.isExpanded()){
11856             this.collapse();
11857             if (!this.blockFocus) {
11858                 this.inputEl().focus();
11859             }
11860             
11861         }else {
11862             this.hasFocus = true;
11863             if(this.triggerAction == 'all') {
11864                 this.doQuery(this.allQuery, true);
11865             } else {
11866                 this.doQuery(this.getRawValue());
11867             }
11868             if (!this.blockFocus) {
11869                 this.inputEl().focus();
11870             }
11871         }
11872     },
11873     
11874     onTickableTriggerClick : function(e)
11875     {
11876         if(this.disabled){
11877             return;
11878         }
11879         
11880         this.page = 0;
11881         this.loadNext = false;
11882         this.hasFocus = true;
11883         
11884         if(this.triggerAction == 'all') {
11885             this.doQuery(this.allQuery, true);
11886         } else {
11887             this.doQuery(this.getRawValue());
11888         }
11889     },
11890     
11891     onSearchFieldClick : function(e)
11892     {
11893         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
11894             return;
11895         }
11896         
11897         this.page = 0;
11898         this.loadNext = false;
11899         this.hasFocus = true;
11900         
11901         if(this.triggerAction == 'all') {
11902             this.doQuery(this.allQuery, true);
11903         } else {
11904             this.doQuery(this.getRawValue());
11905         }
11906     },
11907     
11908     listKeyPress : function(e)
11909     {
11910         //Roo.log('listkeypress');
11911         // scroll to first matching element based on key pres..
11912         if (e.isSpecialKey()) {
11913             return false;
11914         }
11915         var k = String.fromCharCode(e.getKey()).toUpperCase();
11916         //Roo.log(k);
11917         var match  = false;
11918         var csel = this.view.getSelectedNodes();
11919         var cselitem = false;
11920         if (csel.length) {
11921             var ix = this.view.indexOf(csel[0]);
11922             cselitem  = this.store.getAt(ix);
11923             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
11924                 cselitem = false;
11925             }
11926             
11927         }
11928         
11929         this.store.each(function(v) { 
11930             if (cselitem) {
11931                 // start at existing selection.
11932                 if (cselitem.id == v.id) {
11933                     cselitem = false;
11934                 }
11935                 return true;
11936             }
11937                 
11938             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
11939                 match = this.store.indexOf(v);
11940                 return false;
11941             }
11942             return true;
11943         }, this);
11944         
11945         if (match === false) {
11946             return true; // no more action?
11947         }
11948         // scroll to?
11949         this.view.select(match);
11950         var sn = Roo.get(this.view.getSelectedNodes()[0])
11951         sn.scrollIntoView(sn.dom.parentNode, false);
11952     },
11953     
11954     onViewScroll : function(e, t){
11955         
11956         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){
11957             return;
11958         }
11959         
11960         this.hasQuery = true;
11961         
11962         this.loading = this.list.select('.loading', true).first();
11963         
11964         if(this.loading === null){
11965             this.list.createChild({
11966                 tag: 'div',
11967                 cls: 'loading select2-more-results select2-active',
11968                 html: 'Loading more results...'
11969             })
11970             
11971             this.loading = this.list.select('.loading', true).first();
11972             
11973             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
11974             
11975             this.loading.hide();
11976         }
11977         
11978         this.loading.show();
11979         
11980         var _combo = this;
11981         
11982         this.page++;
11983         this.loadNext = true;
11984         
11985         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
11986         
11987         return;
11988     },
11989     
11990     addItem : function(o)
11991     {   
11992         var dv = ''; // display value
11993         
11994         if (this.displayField) {
11995             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11996         } else {
11997             // this is an error condition!!!
11998             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
11999         }
12000         
12001         if(!dv.length){
12002             return;
12003         }
12004         
12005         var choice = this.choices.createChild({
12006             tag: 'li',
12007             cls: 'select2-search-choice',
12008             cn: [
12009                 {
12010                     tag: 'div',
12011                     html: dv
12012                 },
12013                 {
12014                     tag: 'a',
12015                     href: '#',
12016                     cls: 'select2-search-choice-close',
12017                     tabindex: '-1'
12018                 }
12019             ]
12020             
12021         }, this.searchField);
12022         
12023         var close = choice.select('a.select2-search-choice-close', true).first()
12024         
12025         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
12026         
12027         this.item.push(o);
12028         
12029         this.lastData = o;
12030         
12031         this.syncValue();
12032         
12033         this.inputEl().dom.value = '';
12034         
12035     },
12036     
12037     onRemoveItem : function(e, _self, o)
12038     {
12039         e.preventDefault();
12040         var index = this.item.indexOf(o.data) * 1;
12041         
12042         if( index < 0){
12043             Roo.log('not this item?!');
12044             return;
12045         }
12046         
12047         this.item.splice(index, 1);
12048         o.item.remove();
12049         
12050         this.syncValue();
12051         
12052         this.fireEvent('remove', this, e);
12053         
12054     },
12055     
12056     syncValue : function()
12057     {
12058         if(!this.item.length){
12059             this.clearValue();
12060             return;
12061         }
12062             
12063         var value = [];
12064         var _this = this;
12065         Roo.each(this.item, function(i){
12066             if(_this.valueField){
12067                 value.push(i[_this.valueField]);
12068                 return;
12069             }
12070
12071             value.push(i);
12072         });
12073
12074         this.value = value.join(',');
12075
12076         if(this.hiddenField){
12077             this.hiddenField.dom.value = this.value;
12078         }
12079     },
12080     
12081     clearItem : function()
12082     {
12083         if(!this.multiple){
12084             return;
12085         }
12086         
12087         this.item = [];
12088         
12089         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
12090            c.remove();
12091         });
12092         
12093         this.syncValue();
12094     },
12095     
12096     inputEl: function ()
12097     {
12098         if(this.tickable){
12099             return this.searchField;
12100         }
12101         return this.el.select('input.form-control',true).first();
12102     },
12103     
12104     
12105     onTickableFooterButtonClick : function(e, btn, el)
12106     {
12107         e.preventDefault();
12108         
12109         if(btn && btn.name == 'cancel'){
12110             this.tickItems = Roo.apply([], this.item);
12111             this.collapse();
12112             return;
12113         }
12114         
12115         this.clearItem();
12116         
12117         var _this = this;
12118         
12119         Roo.each(this.tickItems, function(o){
12120             _this.addItem(o);
12121         });
12122         
12123         this.collapse();
12124         
12125     },
12126     
12127     validate : function()
12128     {
12129         var v = this.getRawValue();
12130         
12131         if(this.multiple){
12132             v = this.getValue();
12133         }
12134         
12135         if(this.disabled || this.validateValue(v)){
12136             this.clearInvalid();
12137             return true;
12138         }
12139         return false;
12140     }
12141     
12142     
12143
12144     /** 
12145     * @cfg {Boolean} grow 
12146     * @hide 
12147     */
12148     /** 
12149     * @cfg {Number} growMin 
12150     * @hide 
12151     */
12152     /** 
12153     * @cfg {Number} growMax 
12154     * @hide 
12155     */
12156     /**
12157      * @hide
12158      * @method autoSize
12159      */
12160 });
12161 /*
12162  * Based on:
12163  * Ext JS Library 1.1.1
12164  * Copyright(c) 2006-2007, Ext JS, LLC.
12165  *
12166  * Originally Released Under LGPL - original licence link has changed is not relivant.
12167  *
12168  * Fork - LGPL
12169  * <script type="text/javascript">
12170  */
12171
12172 /**
12173  * @class Roo.View
12174  * @extends Roo.util.Observable
12175  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
12176  * This class also supports single and multi selection modes. <br>
12177  * Create a data model bound view:
12178  <pre><code>
12179  var store = new Roo.data.Store(...);
12180
12181  var view = new Roo.View({
12182     el : "my-element",
12183     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
12184  
12185     singleSelect: true,
12186     selectedClass: "ydataview-selected",
12187     store: store
12188  });
12189
12190  // listen for node click?
12191  view.on("click", function(vw, index, node, e){
12192  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
12193  });
12194
12195  // load XML data
12196  dataModel.load("foobar.xml");
12197  </code></pre>
12198  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
12199  * <br><br>
12200  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
12201  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
12202  * 
12203  * Note: old style constructor is still suported (container, template, config)
12204  * 
12205  * @constructor
12206  * Create a new View
12207  * @param {Object} config The config object
12208  * 
12209  */
12210 Roo.View = function(config, depreciated_tpl, depreciated_config){
12211     
12212     this.parent = false;
12213     
12214     if (typeof(depreciated_tpl) == 'undefined') {
12215         // new way.. - universal constructor.
12216         Roo.apply(this, config);
12217         this.el  = Roo.get(this.el);
12218     } else {
12219         // old format..
12220         this.el  = Roo.get(config);
12221         this.tpl = depreciated_tpl;
12222         Roo.apply(this, depreciated_config);
12223     }
12224     this.wrapEl  = this.el.wrap().wrap();
12225     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
12226     
12227     
12228     if(typeof(this.tpl) == "string"){
12229         this.tpl = new Roo.Template(this.tpl);
12230     } else {
12231         // support xtype ctors..
12232         this.tpl = new Roo.factory(this.tpl, Roo);
12233     }
12234     
12235     
12236     this.tpl.compile();
12237     
12238     /** @private */
12239     this.addEvents({
12240         /**
12241          * @event beforeclick
12242          * Fires before a click is processed. Returns false to cancel the default action.
12243          * @param {Roo.View} this
12244          * @param {Number} index The index of the target node
12245          * @param {HTMLElement} node The target node
12246          * @param {Roo.EventObject} e The raw event object
12247          */
12248             "beforeclick" : true,
12249         /**
12250          * @event click
12251          * Fires when a template node is clicked.
12252          * @param {Roo.View} this
12253          * @param {Number} index The index of the target node
12254          * @param {HTMLElement} node The target node
12255          * @param {Roo.EventObject} e The raw event object
12256          */
12257             "click" : true,
12258         /**
12259          * @event dblclick
12260          * Fires when a template node is double clicked.
12261          * @param {Roo.View} this
12262          * @param {Number} index The index of the target node
12263          * @param {HTMLElement} node The target node
12264          * @param {Roo.EventObject} e The raw event object
12265          */
12266             "dblclick" : true,
12267         /**
12268          * @event contextmenu
12269          * Fires when a template node is right clicked.
12270          * @param {Roo.View} this
12271          * @param {Number} index The index of the target node
12272          * @param {HTMLElement} node The target node
12273          * @param {Roo.EventObject} e The raw event object
12274          */
12275             "contextmenu" : true,
12276         /**
12277          * @event selectionchange
12278          * Fires when the selected nodes change.
12279          * @param {Roo.View} this
12280          * @param {Array} selections Array of the selected nodes
12281          */
12282             "selectionchange" : true,
12283     
12284         /**
12285          * @event beforeselect
12286          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
12287          * @param {Roo.View} this
12288          * @param {HTMLElement} node The node to be selected
12289          * @param {Array} selections Array of currently selected nodes
12290          */
12291             "beforeselect" : true,
12292         /**
12293          * @event preparedata
12294          * Fires on every row to render, to allow you to change the data.
12295          * @param {Roo.View} this
12296          * @param {Object} data to be rendered (change this)
12297          */
12298           "preparedata" : true
12299           
12300           
12301         });
12302
12303
12304
12305     this.el.on({
12306         "click": this.onClick,
12307         "dblclick": this.onDblClick,
12308         "contextmenu": this.onContextMenu,
12309         scope:this
12310     });
12311
12312     this.selections = [];
12313     this.nodes = [];
12314     this.cmp = new Roo.CompositeElementLite([]);
12315     if(this.store){
12316         this.store = Roo.factory(this.store, Roo.data);
12317         this.setStore(this.store, true);
12318     }
12319     
12320     if ( this.footer && this.footer.xtype) {
12321            
12322          var fctr = this.wrapEl.appendChild(document.createElement("div"));
12323         
12324         this.footer.dataSource = this.store
12325         this.footer.container = fctr;
12326         this.footer = Roo.factory(this.footer, Roo);
12327         fctr.insertFirst(this.el);
12328         
12329         // this is a bit insane - as the paging toolbar seems to detach the el..
12330 //        dom.parentNode.parentNode.parentNode
12331          // they get detached?
12332     }
12333     
12334     
12335     Roo.View.superclass.constructor.call(this);
12336     
12337     
12338 };
12339
12340 Roo.extend(Roo.View, Roo.util.Observable, {
12341     
12342      /**
12343      * @cfg {Roo.data.Store} store Data store to load data from.
12344      */
12345     store : false,
12346     
12347     /**
12348      * @cfg {String|Roo.Element} el The container element.
12349      */
12350     el : '',
12351     
12352     /**
12353      * @cfg {String|Roo.Template} tpl The template used by this View 
12354      */
12355     tpl : false,
12356     /**
12357      * @cfg {String} dataName the named area of the template to use as the data area
12358      *                          Works with domtemplates roo-name="name"
12359      */
12360     dataName: false,
12361     /**
12362      * @cfg {String} selectedClass The css class to add to selected nodes
12363      */
12364     selectedClass : "x-view-selected",
12365      /**
12366      * @cfg {String} emptyText The empty text to show when nothing is loaded.
12367      */
12368     emptyText : "",
12369     
12370     /**
12371      * @cfg {String} text to display on mask (default Loading)
12372      */
12373     mask : false,
12374     /**
12375      * @cfg {Boolean} multiSelect Allow multiple selection
12376      */
12377     multiSelect : false,
12378     /**
12379      * @cfg {Boolean} singleSelect Allow single selection
12380      */
12381     singleSelect:  false,
12382     
12383     /**
12384      * @cfg {Boolean} toggleSelect - selecting 
12385      */
12386     toggleSelect : false,
12387     
12388     /**
12389      * @cfg {Boolean} tickable - selecting 
12390      */
12391     tickable : false,
12392     
12393     /**
12394      * Returns the element this view is bound to.
12395      * @return {Roo.Element}
12396      */
12397     getEl : function(){
12398         return this.wrapEl;
12399     },
12400     
12401     
12402
12403     /**
12404      * Refreshes the view. - called by datachanged on the store. - do not call directly.
12405      */
12406     refresh : function(){
12407         Roo.log('refresh');
12408         var t = this.tpl;
12409         
12410         // if we are using something like 'domtemplate', then
12411         // the what gets used is:
12412         // t.applySubtemplate(NAME, data, wrapping data..)
12413         // the outer template then get' applied with
12414         //     the store 'extra data'
12415         // and the body get's added to the
12416         //      roo-name="data" node?
12417         //      <span class='roo-tpl-{name}'></span> ?????
12418         
12419         
12420         
12421         this.clearSelections();
12422         this.el.update("");
12423         var html = [];
12424         var records = this.store.getRange();
12425         if(records.length < 1) {
12426             
12427             // is this valid??  = should it render a template??
12428             
12429             this.el.update(this.emptyText);
12430             return;
12431         }
12432         var el = this.el;
12433         if (this.dataName) {
12434             this.el.update(t.apply(this.store.meta)); //????
12435             el = this.el.child('.roo-tpl-' + this.dataName);
12436         }
12437         
12438         for(var i = 0, len = records.length; i < len; i++){
12439             var data = this.prepareData(records[i].data, i, records[i]);
12440             this.fireEvent("preparedata", this, data, i, records[i]);
12441             
12442             var d = Roo.apply({}, data);
12443             
12444             if(this.tickable){
12445                 Roo.apply(d, {'roo-id' : Roo.id()});
12446                 
12447                 var _this = this;
12448             
12449                 Roo.each(this.parent.item, function(item){
12450                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
12451                         return;
12452                     }
12453                     Roo.apply(d, {'roo-data-checked' : 'checked'});
12454                 });
12455             }
12456             
12457             html[html.length] = Roo.util.Format.trim(
12458                 this.dataName ?
12459                     t.applySubtemplate(this.dataName, d, this.store.meta) :
12460                     t.apply(d)
12461             );
12462         }
12463         
12464         
12465         
12466         el.update(html.join(""));
12467         this.nodes = el.dom.childNodes;
12468         this.updateIndexes(0);
12469     },
12470     
12471
12472     /**
12473      * Function to override to reformat the data that is sent to
12474      * the template for each node.
12475      * DEPRICATED - use the preparedata event handler.
12476      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
12477      * a JSON object for an UpdateManager bound view).
12478      */
12479     prepareData : function(data, index, record)
12480     {
12481         this.fireEvent("preparedata", this, data, index, record);
12482         return data;
12483     },
12484
12485     onUpdate : function(ds, record){
12486          Roo.log('on update');   
12487         this.clearSelections();
12488         var index = this.store.indexOf(record);
12489         var n = this.nodes[index];
12490         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
12491         n.parentNode.removeChild(n);
12492         this.updateIndexes(index, index);
12493     },
12494
12495     
12496     
12497 // --------- FIXME     
12498     onAdd : function(ds, records, index)
12499     {
12500         Roo.log(['on Add', ds, records, index] );        
12501         this.clearSelections();
12502         if(this.nodes.length == 0){
12503             this.refresh();
12504             return;
12505         }
12506         var n = this.nodes[index];
12507         for(var i = 0, len = records.length; i < len; i++){
12508             var d = this.prepareData(records[i].data, i, records[i]);
12509             if(n){
12510                 this.tpl.insertBefore(n, d);
12511             }else{
12512                 
12513                 this.tpl.append(this.el, d);
12514             }
12515         }
12516         this.updateIndexes(index);
12517     },
12518
12519     onRemove : function(ds, record, index){
12520         Roo.log('onRemove');
12521         this.clearSelections();
12522         var el = this.dataName  ?
12523             this.el.child('.roo-tpl-' + this.dataName) :
12524             this.el; 
12525         
12526         el.dom.removeChild(this.nodes[index]);
12527         this.updateIndexes(index);
12528     },
12529
12530     /**
12531      * Refresh an individual node.
12532      * @param {Number} index
12533      */
12534     refreshNode : function(index){
12535         this.onUpdate(this.store, this.store.getAt(index));
12536     },
12537
12538     updateIndexes : function(startIndex, endIndex){
12539         var ns = this.nodes;
12540         startIndex = startIndex || 0;
12541         endIndex = endIndex || ns.length - 1;
12542         for(var i = startIndex; i <= endIndex; i++){
12543             ns[i].nodeIndex = i;
12544         }
12545     },
12546
12547     /**
12548      * Changes the data store this view uses and refresh the view.
12549      * @param {Store} store
12550      */
12551     setStore : function(store, initial){
12552         if(!initial && this.store){
12553             this.store.un("datachanged", this.refresh);
12554             this.store.un("add", this.onAdd);
12555             this.store.un("remove", this.onRemove);
12556             this.store.un("update", this.onUpdate);
12557             this.store.un("clear", this.refresh);
12558             this.store.un("beforeload", this.onBeforeLoad);
12559             this.store.un("load", this.onLoad);
12560             this.store.un("loadexception", this.onLoad);
12561         }
12562         if(store){
12563           
12564             store.on("datachanged", this.refresh, this);
12565             store.on("add", this.onAdd, this);
12566             store.on("remove", this.onRemove, this);
12567             store.on("update", this.onUpdate, this);
12568             store.on("clear", this.refresh, this);
12569             store.on("beforeload", this.onBeforeLoad, this);
12570             store.on("load", this.onLoad, this);
12571             store.on("loadexception", this.onLoad, this);
12572         }
12573         
12574         if(store){
12575             this.refresh();
12576         }
12577     },
12578     /**
12579      * onbeforeLoad - masks the loading area.
12580      *
12581      */
12582     onBeforeLoad : function(store,opts)
12583     {
12584          Roo.log('onBeforeLoad');   
12585         if (!opts.add) {
12586             this.el.update("");
12587         }
12588         this.el.mask(this.mask ? this.mask : "Loading" ); 
12589     },
12590     onLoad : function ()
12591     {
12592         this.el.unmask();
12593     },
12594     
12595
12596     /**
12597      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
12598      * @param {HTMLElement} node
12599      * @return {HTMLElement} The template node
12600      */
12601     findItemFromChild : function(node){
12602         var el = this.dataName  ?
12603             this.el.child('.roo-tpl-' + this.dataName,true) :
12604             this.el.dom; 
12605         
12606         if(!node || node.parentNode == el){
12607                     return node;
12608             }
12609             var p = node.parentNode;
12610             while(p && p != el){
12611             if(p.parentNode == el){
12612                 return p;
12613             }
12614             p = p.parentNode;
12615         }
12616             return null;
12617     },
12618
12619     /** @ignore */
12620     onClick : function(e){
12621         var item = this.findItemFromChild(e.getTarget());
12622         if(item){
12623             var index = this.indexOf(item);
12624             if(this.onItemClick(item, index, e) !== false){
12625                 this.fireEvent("click", this, index, item, e);
12626             }
12627         }else{
12628             this.clearSelections();
12629         }
12630     },
12631
12632     /** @ignore */
12633     onContextMenu : function(e){
12634         var item = this.findItemFromChild(e.getTarget());
12635         if(item){
12636             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
12637         }
12638     },
12639
12640     /** @ignore */
12641     onDblClick : function(e){
12642         var item = this.findItemFromChild(e.getTarget());
12643         if(item){
12644             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
12645         }
12646     },
12647
12648     onItemClick : function(item, index, e)
12649     {
12650         if(this.fireEvent("beforeclick", this, index, item, e) === false){
12651             return false;
12652         }
12653         if (this.toggleSelect) {
12654             var m = this.isSelected(item) ? 'unselect' : 'select';
12655             Roo.log(m);
12656             var _t = this;
12657             _t[m](item, true, false);
12658             return true;
12659         }
12660         if(this.multiSelect || this.singleSelect){
12661             if(this.multiSelect && e.shiftKey && this.lastSelection){
12662                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
12663             }else{
12664                 this.select(item, this.multiSelect && e.ctrlKey);
12665                 this.lastSelection = item;
12666             }
12667             
12668             if(!this.tickable){
12669                 e.preventDefault();
12670             }
12671             
12672         }
12673         return true;
12674     },
12675
12676     /**
12677      * Get the number of selected nodes.
12678      * @return {Number}
12679      */
12680     getSelectionCount : function(){
12681         return this.selections.length;
12682     },
12683
12684     /**
12685      * Get the currently selected nodes.
12686      * @return {Array} An array of HTMLElements
12687      */
12688     getSelectedNodes : function(){
12689         return this.selections;
12690     },
12691
12692     /**
12693      * Get the indexes of the selected nodes.
12694      * @return {Array}
12695      */
12696     getSelectedIndexes : function(){
12697         var indexes = [], s = this.selections;
12698         for(var i = 0, len = s.length; i < len; i++){
12699             indexes.push(s[i].nodeIndex);
12700         }
12701         return indexes;
12702     },
12703
12704     /**
12705      * Clear all selections
12706      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
12707      */
12708     clearSelections : function(suppressEvent){
12709         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
12710             this.cmp.elements = this.selections;
12711             this.cmp.removeClass(this.selectedClass);
12712             this.selections = [];
12713             if(!suppressEvent){
12714                 this.fireEvent("selectionchange", this, this.selections);
12715             }
12716         }
12717     },
12718
12719     /**
12720      * Returns true if the passed node is selected
12721      * @param {HTMLElement/Number} node The node or node index
12722      * @return {Boolean}
12723      */
12724     isSelected : function(node){
12725         var s = this.selections;
12726         if(s.length < 1){
12727             return false;
12728         }
12729         node = this.getNode(node);
12730         return s.indexOf(node) !== -1;
12731     },
12732
12733     /**
12734      * Selects nodes.
12735      * @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
12736      * @param {Boolean} keepExisting (optional) true to keep existing selections
12737      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12738      */
12739     select : function(nodeInfo, keepExisting, suppressEvent){
12740         if(nodeInfo instanceof Array){
12741             if(!keepExisting){
12742                 this.clearSelections(true);
12743             }
12744             for(var i = 0, len = nodeInfo.length; i < len; i++){
12745                 this.select(nodeInfo[i], true, true);
12746             }
12747             return;
12748         } 
12749         var node = this.getNode(nodeInfo);
12750         if(!node || this.isSelected(node)){
12751             return; // already selected.
12752         }
12753         if(!keepExisting){
12754             this.clearSelections(true);
12755         }
12756         
12757         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
12758             Roo.fly(node).addClass(this.selectedClass);
12759             this.selections.push(node);
12760             if(!suppressEvent){
12761                 this.fireEvent("selectionchange", this, this.selections);
12762             }
12763         }
12764         
12765         
12766     },
12767       /**
12768      * Unselects nodes.
12769      * @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
12770      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
12771      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12772      */
12773     unselect : function(nodeInfo, keepExisting, suppressEvent)
12774     {
12775         if(nodeInfo instanceof Array){
12776             Roo.each(this.selections, function(s) {
12777                 this.unselect(s, nodeInfo);
12778             }, this);
12779             return;
12780         }
12781         var node = this.getNode(nodeInfo);
12782         if(!node || !this.isSelected(node)){
12783             Roo.log("not selected");
12784             return; // not selected.
12785         }
12786         // fireevent???
12787         var ns = [];
12788         Roo.each(this.selections, function(s) {
12789             if (s == node ) {
12790                 Roo.fly(node).removeClass(this.selectedClass);
12791
12792                 return;
12793             }
12794             ns.push(s);
12795         },this);
12796         
12797         this.selections= ns;
12798         this.fireEvent("selectionchange", this, this.selections);
12799     },
12800
12801     /**
12802      * Gets a template node.
12803      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12804      * @return {HTMLElement} The node or null if it wasn't found
12805      */
12806     getNode : function(nodeInfo){
12807         if(typeof nodeInfo == "string"){
12808             return document.getElementById(nodeInfo);
12809         }else if(typeof nodeInfo == "number"){
12810             return this.nodes[nodeInfo];
12811         }
12812         return nodeInfo;
12813     },
12814
12815     /**
12816      * Gets a range template nodes.
12817      * @param {Number} startIndex
12818      * @param {Number} endIndex
12819      * @return {Array} An array of nodes
12820      */
12821     getNodes : function(start, end){
12822         var ns = this.nodes;
12823         start = start || 0;
12824         end = typeof end == "undefined" ? ns.length - 1 : end;
12825         var nodes = [];
12826         if(start <= end){
12827             for(var i = start; i <= end; i++){
12828                 nodes.push(ns[i]);
12829             }
12830         } else{
12831             for(var i = start; i >= end; i--){
12832                 nodes.push(ns[i]);
12833             }
12834         }
12835         return nodes;
12836     },
12837
12838     /**
12839      * Finds the index of the passed node
12840      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12841      * @return {Number} The index of the node or -1
12842      */
12843     indexOf : function(node){
12844         node = this.getNode(node);
12845         if(typeof node.nodeIndex == "number"){
12846             return node.nodeIndex;
12847         }
12848         var ns = this.nodes;
12849         for(var i = 0, len = ns.length; i < len; i++){
12850             if(ns[i] == node){
12851                 return i;
12852             }
12853         }
12854         return -1;
12855     }
12856 });
12857 /*
12858  * - LGPL
12859  *
12860  * based on jquery fullcalendar
12861  * 
12862  */
12863
12864 Roo.bootstrap = Roo.bootstrap || {};
12865 /**
12866  * @class Roo.bootstrap.Calendar
12867  * @extends Roo.bootstrap.Component
12868  * Bootstrap Calendar class
12869  * @cfg {Boolean} loadMask (true|false) default false
12870  * @cfg {Object} header generate the user specific header of the calendar, default false
12871
12872  * @constructor
12873  * Create a new Container
12874  * @param {Object} config The config object
12875  */
12876
12877
12878
12879 Roo.bootstrap.Calendar = function(config){
12880     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
12881      this.addEvents({
12882         /**
12883              * @event select
12884              * Fires when a date is selected
12885              * @param {DatePicker} this
12886              * @param {Date} date The selected date
12887              */
12888         'select': true,
12889         /**
12890              * @event monthchange
12891              * Fires when the displayed month changes 
12892              * @param {DatePicker} this
12893              * @param {Date} date The selected month
12894              */
12895         'monthchange': true,
12896         /**
12897              * @event evententer
12898              * Fires when mouse over an event
12899              * @param {Calendar} this
12900              * @param {event} Event
12901              */
12902         'evententer': true,
12903         /**
12904              * @event eventleave
12905              * Fires when the mouse leaves an
12906              * @param {Calendar} this
12907              * @param {event}
12908              */
12909         'eventleave': true,
12910         /**
12911              * @event eventclick
12912              * Fires when the mouse click an
12913              * @param {Calendar} this
12914              * @param {event}
12915              */
12916         'eventclick': true
12917         
12918     });
12919
12920 };
12921
12922 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
12923     
12924      /**
12925      * @cfg {Number} startDay
12926      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
12927      */
12928     startDay : 0,
12929     
12930     loadMask : false,
12931     
12932     header : false,
12933       
12934     getAutoCreate : function(){
12935         
12936         
12937         var fc_button = function(name, corner, style, content ) {
12938             return Roo.apply({},{
12939                 tag : 'span',
12940                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
12941                          (corner.length ?
12942                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
12943                             ''
12944                         ),
12945                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
12946                 unselectable: 'on'
12947             });
12948         };
12949         
12950         var header = {};
12951         
12952         if(!this.header){
12953             header = {
12954                 tag : 'table',
12955                 cls : 'fc-header',
12956                 style : 'width:100%',
12957                 cn : [
12958                     {
12959                         tag: 'tr',
12960                         cn : [
12961                             {
12962                                 tag : 'td',
12963                                 cls : 'fc-header-left',
12964                                 cn : [
12965                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
12966                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
12967                                     { tag: 'span', cls: 'fc-header-space' },
12968                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
12969
12970
12971                                 ]
12972                             },
12973
12974                             {
12975                                 tag : 'td',
12976                                 cls : 'fc-header-center',
12977                                 cn : [
12978                                     {
12979                                         tag: 'span',
12980                                         cls: 'fc-header-title',
12981                                         cn : {
12982                                             tag: 'H2',
12983                                             html : 'month / year'
12984                                         }
12985                                     }
12986
12987                                 ]
12988                             },
12989                             {
12990                                 tag : 'td',
12991                                 cls : 'fc-header-right',
12992                                 cn : [
12993                               /*      fc_button('month', 'left', '', 'month' ),
12994                                     fc_button('week', '', '', 'week' ),
12995                                     fc_button('day', 'right', '', 'day' )
12996                                 */    
12997
12998                                 ]
12999                             }
13000
13001                         ]
13002                     }
13003                 ]
13004             };
13005         }
13006         
13007         header = this.header;
13008         
13009        
13010         var cal_heads = function() {
13011             var ret = [];
13012             // fixme - handle this.
13013             
13014             for (var i =0; i < Date.dayNames.length; i++) {
13015                 var d = Date.dayNames[i];
13016                 ret.push({
13017                     tag: 'th',
13018                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
13019                     html : d.substring(0,3)
13020                 });
13021                 
13022             }
13023             ret[0].cls += ' fc-first';
13024             ret[6].cls += ' fc-last';
13025             return ret;
13026         };
13027         var cal_cell = function(n) {
13028             return  {
13029                 tag: 'td',
13030                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
13031                 cn : [
13032                     {
13033                         cn : [
13034                             {
13035                                 cls: 'fc-day-number',
13036                                 html: 'D'
13037                             },
13038                             {
13039                                 cls: 'fc-day-content',
13040                              
13041                                 cn : [
13042                                      {
13043                                         style: 'position: relative;' // height: 17px;
13044                                     }
13045                                 ]
13046                             }
13047                             
13048                             
13049                         ]
13050                     }
13051                 ]
13052                 
13053             }
13054         };
13055         var cal_rows = function() {
13056             
13057             var ret = []
13058             for (var r = 0; r < 6; r++) {
13059                 var row= {
13060                     tag : 'tr',
13061                     cls : 'fc-week',
13062                     cn : []
13063                 };
13064                 
13065                 for (var i =0; i < Date.dayNames.length; i++) {
13066                     var d = Date.dayNames[i];
13067                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
13068
13069                 }
13070                 row.cn[0].cls+=' fc-first';
13071                 row.cn[0].cn[0].style = 'min-height:90px';
13072                 row.cn[6].cls+=' fc-last';
13073                 ret.push(row);
13074                 
13075             }
13076             ret[0].cls += ' fc-first';
13077             ret[4].cls += ' fc-prev-last';
13078             ret[5].cls += ' fc-last';
13079             return ret;
13080             
13081         };
13082         
13083         var cal_table = {
13084             tag: 'table',
13085             cls: 'fc-border-separate',
13086             style : 'width:100%',
13087             cellspacing  : 0,
13088             cn : [
13089                 { 
13090                     tag: 'thead',
13091                     cn : [
13092                         { 
13093                             tag: 'tr',
13094                             cls : 'fc-first fc-last',
13095                             cn : cal_heads()
13096                         }
13097                     ]
13098                 },
13099                 { 
13100                     tag: 'tbody',
13101                     cn : cal_rows()
13102                 }
13103                   
13104             ]
13105         };
13106          
13107          var cfg = {
13108             cls : 'fc fc-ltr',
13109             cn : [
13110                 header,
13111                 {
13112                     cls : 'fc-content',
13113                     style : "position: relative;",
13114                     cn : [
13115                         {
13116                             cls : 'fc-view fc-view-month fc-grid',
13117                             style : 'position: relative',
13118                             unselectable : 'on',
13119                             cn : [
13120                                 {
13121                                     cls : 'fc-event-container',
13122                                     style : 'position:absolute;z-index:8;top:0;left:0;'
13123                                 },
13124                                 cal_table
13125                             ]
13126                         }
13127                     ]
13128     
13129                 }
13130            ] 
13131             
13132         };
13133         
13134          
13135         
13136         return cfg;
13137     },
13138     
13139     
13140     initEvents : function()
13141     {
13142         if(!this.store){
13143             throw "can not find store for calendar";
13144         }
13145         
13146         var mark = {
13147             tag: "div",
13148             cls:"x-dlg-mask",
13149             style: "text-align:center",
13150             cn: [
13151                 {
13152                     tag: "div",
13153                     style: "background-color:white;width:50%;margin:250 auto",
13154                     cn: [
13155                         {
13156                             tag: "img",
13157                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
13158                         },
13159                         {
13160                             tag: "span",
13161                             html: "Loading"
13162                         }
13163                         
13164                     ]
13165                 }
13166             ]
13167         }
13168         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
13169         
13170         var size = this.el.select('.fc-content', true).first().getSize();
13171         this.maskEl.setSize(size.width, size.height);
13172         this.maskEl.enableDisplayMode("block");
13173         if(!this.loadMask){
13174             this.maskEl.hide();
13175         }
13176         
13177         this.store = Roo.factory(this.store, Roo.data);
13178         this.store.on('load', this.onLoad, this);
13179         this.store.on('beforeload', this.onBeforeLoad, this);
13180         
13181         this.resize();
13182         
13183         this.cells = this.el.select('.fc-day',true);
13184         //Roo.log(this.cells);
13185         this.textNodes = this.el.query('.fc-day-number');
13186         this.cells.addClassOnOver('fc-state-hover');
13187         
13188         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
13189         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
13190         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
13191         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
13192         
13193         this.on('monthchange', this.onMonthChange, this);
13194         
13195         this.update(new Date().clearTime());
13196     },
13197     
13198     resize : function() {
13199         var sz  = this.el.getSize();
13200         
13201         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
13202         this.el.select('.fc-day-content div',true).setHeight(34);
13203     },
13204     
13205     
13206     // private
13207     showPrevMonth : function(e){
13208         this.update(this.activeDate.add("mo", -1));
13209     },
13210     showToday : function(e){
13211         this.update(new Date().clearTime());
13212     },
13213     // private
13214     showNextMonth : function(e){
13215         this.update(this.activeDate.add("mo", 1));
13216     },
13217
13218     // private
13219     showPrevYear : function(){
13220         this.update(this.activeDate.add("y", -1));
13221     },
13222
13223     // private
13224     showNextYear : function(){
13225         this.update(this.activeDate.add("y", 1));
13226     },
13227
13228     
13229    // private
13230     update : function(date)
13231     {
13232         var vd = this.activeDate;
13233         this.activeDate = date;
13234 //        if(vd && this.el){
13235 //            var t = date.getTime();
13236 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
13237 //                Roo.log('using add remove');
13238 //                
13239 //                this.fireEvent('monthchange', this, date);
13240 //                
13241 //                this.cells.removeClass("fc-state-highlight");
13242 //                this.cells.each(function(c){
13243 //                   if(c.dateValue == t){
13244 //                       c.addClass("fc-state-highlight");
13245 //                       setTimeout(function(){
13246 //                            try{c.dom.firstChild.focus();}catch(e){}
13247 //                       }, 50);
13248 //                       return false;
13249 //                   }
13250 //                   return true;
13251 //                });
13252 //                return;
13253 //            }
13254 //        }
13255         
13256         var days = date.getDaysInMonth();
13257         
13258         var firstOfMonth = date.getFirstDateOfMonth();
13259         var startingPos = firstOfMonth.getDay()-this.startDay;
13260         
13261         if(startingPos < this.startDay){
13262             startingPos += 7;
13263         }
13264         
13265         var pm = date.add(Date.MONTH, -1);
13266         var prevStart = pm.getDaysInMonth()-startingPos;
13267 //        
13268         this.cells = this.el.select('.fc-day',true);
13269         this.textNodes = this.el.query('.fc-day-number');
13270         this.cells.addClassOnOver('fc-state-hover');
13271         
13272         var cells = this.cells.elements;
13273         var textEls = this.textNodes;
13274         
13275         Roo.each(cells, function(cell){
13276             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
13277         });
13278         
13279         days += startingPos;
13280
13281         // convert everything to numbers so it's fast
13282         var day = 86400000;
13283         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
13284         //Roo.log(d);
13285         //Roo.log(pm);
13286         //Roo.log(prevStart);
13287         
13288         var today = new Date().clearTime().getTime();
13289         var sel = date.clearTime().getTime();
13290         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
13291         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
13292         var ddMatch = this.disabledDatesRE;
13293         var ddText = this.disabledDatesText;
13294         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
13295         var ddaysText = this.disabledDaysText;
13296         var format = this.format;
13297         
13298         var setCellClass = function(cal, cell){
13299             cell.row = 0;
13300             cell.events = [];
13301             cell.more = [];
13302             //Roo.log('set Cell Class');
13303             cell.title = "";
13304             var t = d.getTime();
13305             
13306             //Roo.log(d);
13307             
13308             cell.dateValue = t;
13309             if(t == today){
13310                 cell.className += " fc-today";
13311                 cell.className += " fc-state-highlight";
13312                 cell.title = cal.todayText;
13313             }
13314             if(t == sel){
13315                 // disable highlight in other month..
13316                 //cell.className += " fc-state-highlight";
13317                 
13318             }
13319             // disabling
13320             if(t < min) {
13321                 cell.className = " fc-state-disabled";
13322                 cell.title = cal.minText;
13323                 return;
13324             }
13325             if(t > max) {
13326                 cell.className = " fc-state-disabled";
13327                 cell.title = cal.maxText;
13328                 return;
13329             }
13330             if(ddays){
13331                 if(ddays.indexOf(d.getDay()) != -1){
13332                     cell.title = ddaysText;
13333                     cell.className = " fc-state-disabled";
13334                 }
13335             }
13336             if(ddMatch && format){
13337                 var fvalue = d.dateFormat(format);
13338                 if(ddMatch.test(fvalue)){
13339                     cell.title = ddText.replace("%0", fvalue);
13340                     cell.className = " fc-state-disabled";
13341                 }
13342             }
13343             
13344             if (!cell.initialClassName) {
13345                 cell.initialClassName = cell.dom.className;
13346             }
13347             
13348             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
13349         };
13350
13351         var i = 0;
13352         
13353         for(; i < startingPos; i++) {
13354             textEls[i].innerHTML = (++prevStart);
13355             d.setDate(d.getDate()+1);
13356             
13357             cells[i].className = "fc-past fc-other-month";
13358             setCellClass(this, cells[i]);
13359         }
13360         
13361         var intDay = 0;
13362         
13363         for(; i < days; i++){
13364             intDay = i - startingPos + 1;
13365             textEls[i].innerHTML = (intDay);
13366             d.setDate(d.getDate()+1);
13367             
13368             cells[i].className = ''; // "x-date-active";
13369             setCellClass(this, cells[i]);
13370         }
13371         var extraDays = 0;
13372         
13373         for(; i < 42; i++) {
13374             textEls[i].innerHTML = (++extraDays);
13375             d.setDate(d.getDate()+1);
13376             
13377             cells[i].className = "fc-future fc-other-month";
13378             setCellClass(this, cells[i]);
13379         }
13380         
13381         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
13382         
13383         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
13384         
13385         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
13386         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
13387         
13388         if(totalRows != 6){
13389             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
13390             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
13391         }
13392         
13393         this.fireEvent('monthchange', this, date);
13394         
13395         
13396         /*
13397         if(!this.internalRender){
13398             var main = this.el.dom.firstChild;
13399             var w = main.offsetWidth;
13400             this.el.setWidth(w + this.el.getBorderWidth("lr"));
13401             Roo.fly(main).setWidth(w);
13402             this.internalRender = true;
13403             // opera does not respect the auto grow header center column
13404             // then, after it gets a width opera refuses to recalculate
13405             // without a second pass
13406             if(Roo.isOpera && !this.secondPass){
13407                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
13408                 this.secondPass = true;
13409                 this.update.defer(10, this, [date]);
13410             }
13411         }
13412         */
13413         
13414     },
13415     
13416     findCell : function(dt) {
13417         dt = dt.clearTime().getTime();
13418         var ret = false;
13419         this.cells.each(function(c){
13420             //Roo.log("check " +c.dateValue + '?=' + dt);
13421             if(c.dateValue == dt){
13422                 ret = c;
13423                 return false;
13424             }
13425             return true;
13426         });
13427         
13428         return ret;
13429     },
13430     
13431     findCells : function(ev) {
13432         var s = ev.start.clone().clearTime().getTime();
13433        // Roo.log(s);
13434         var e= ev.end.clone().clearTime().getTime();
13435        // Roo.log(e);
13436         var ret = [];
13437         this.cells.each(function(c){
13438              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
13439             
13440             if(c.dateValue > e){
13441                 return ;
13442             }
13443             if(c.dateValue < s){
13444                 return ;
13445             }
13446             ret.push(c);
13447         });
13448         
13449         return ret;    
13450     },
13451     
13452 //    findBestRow: function(cells)
13453 //    {
13454 //        var ret = 0;
13455 //        
13456 //        for (var i =0 ; i < cells.length;i++) {
13457 //            ret  = Math.max(cells[i].rows || 0,ret);
13458 //        }
13459 //        return ret;
13460 //        
13461 //    },
13462     
13463     
13464     addItem : function(ev)
13465     {
13466         // look for vertical location slot in
13467         var cells = this.findCells(ev);
13468         
13469 //        ev.row = this.findBestRow(cells);
13470         
13471         // work out the location.
13472         
13473         var crow = false;
13474         var rows = [];
13475         for(var i =0; i < cells.length; i++) {
13476             
13477             cells[i].row = cells[0].row;
13478             
13479             if(i == 0){
13480                 cells[i].row = cells[i].row + 1;
13481             }
13482             
13483             if (!crow) {
13484                 crow = {
13485                     start : cells[i],
13486                     end :  cells[i]
13487                 };
13488                 continue;
13489             }
13490             if (crow.start.getY() == cells[i].getY()) {
13491                 // on same row.
13492                 crow.end = cells[i];
13493                 continue;
13494             }
13495             // different row.
13496             rows.push(crow);
13497             crow = {
13498                 start: cells[i],
13499                 end : cells[i]
13500             };
13501             
13502         }
13503         
13504         rows.push(crow);
13505         ev.els = [];
13506         ev.rows = rows;
13507         ev.cells = cells;
13508         
13509         cells[0].events.push(ev);
13510         
13511         this.calevents.push(ev);
13512     },
13513     
13514     clearEvents: function() {
13515         
13516         if(!this.calevents){
13517             return;
13518         }
13519         
13520         Roo.each(this.cells.elements, function(c){
13521             c.row = 0;
13522             c.events = [];
13523             c.more = [];
13524         });
13525         
13526         Roo.each(this.calevents, function(e) {
13527             Roo.each(e.els, function(el) {
13528                 el.un('mouseenter' ,this.onEventEnter, this);
13529                 el.un('mouseleave' ,this.onEventLeave, this);
13530                 el.remove();
13531             },this);
13532         },this);
13533         
13534         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
13535             e.remove();
13536         });
13537         
13538     },
13539     
13540     renderEvents: function()
13541     {   
13542         var _this = this;
13543         
13544         this.cells.each(function(c) {
13545             
13546             if(c.row < 5){
13547                 return;
13548             }
13549             
13550             var ev = c.events;
13551             
13552             var r = 4;
13553             if(c.row != c.events.length){
13554                 r = 4 - (4 - (c.row - c.events.length));
13555             }
13556             
13557             c.events = ev.slice(0, r);
13558             c.more = ev.slice(r);
13559             
13560             if(c.more.length && c.more.length == 1){
13561                 c.events.push(c.more.pop());
13562             }
13563             
13564             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
13565             
13566         });
13567             
13568         this.cells.each(function(c) {
13569             
13570             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
13571             
13572             
13573             for (var e = 0; e < c.events.length; e++){
13574                 var ev = c.events[e];
13575                 var rows = ev.rows;
13576                 
13577                 for(var i = 0; i < rows.length; i++) {
13578                 
13579                     // how many rows should it span..
13580
13581                     var  cfg = {
13582                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
13583                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
13584
13585                         unselectable : "on",
13586                         cn : [
13587                             {
13588                                 cls: 'fc-event-inner',
13589                                 cn : [
13590     //                                {
13591     //                                  tag:'span',
13592     //                                  cls: 'fc-event-time',
13593     //                                  html : cells.length > 1 ? '' : ev.time
13594     //                                },
13595                                     {
13596                                       tag:'span',
13597                                       cls: 'fc-event-title',
13598                                       html : String.format('{0}', ev.title)
13599                                     }
13600
13601
13602                                 ]
13603                             },
13604                             {
13605                                 cls: 'ui-resizable-handle ui-resizable-e',
13606                                 html : '&nbsp;&nbsp;&nbsp'
13607                             }
13608
13609                         ]
13610                     };
13611
13612                     if (i == 0) {
13613                         cfg.cls += ' fc-event-start';
13614                     }
13615                     if ((i+1) == rows.length) {
13616                         cfg.cls += ' fc-event-end';
13617                     }
13618
13619                     var ctr = _this.el.select('.fc-event-container',true).first();
13620                     var cg = ctr.createChild(cfg);
13621
13622                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
13623                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
13624
13625                     var r = (c.more.length) ? 1 : 0;
13626                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
13627                     cg.setWidth(ebox.right - sbox.x -2);
13628
13629                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
13630                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
13631                     cg.on('click', _this.onEventClick, _this, ev);
13632
13633                     ev.els.push(cg);
13634                     
13635                 }
13636                 
13637             }
13638             
13639             
13640             if(c.more.length){
13641                 var  cfg = {
13642                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
13643                     style : 'position: absolute',
13644                     unselectable : "on",
13645                     cn : [
13646                         {
13647                             cls: 'fc-event-inner',
13648                             cn : [
13649                                 {
13650                                   tag:'span',
13651                                   cls: 'fc-event-title',
13652                                   html : 'More'
13653                                 }
13654
13655
13656                             ]
13657                         },
13658                         {
13659                             cls: 'ui-resizable-handle ui-resizable-e',
13660                             html : '&nbsp;&nbsp;&nbsp'
13661                         }
13662
13663                     ]
13664                 };
13665
13666                 var ctr = _this.el.select('.fc-event-container',true).first();
13667                 var cg = ctr.createChild(cfg);
13668
13669                 var sbox = c.select('.fc-day-content',true).first().getBox();
13670                 var ebox = c.select('.fc-day-content',true).first().getBox();
13671                 //Roo.log(cg);
13672                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
13673                 cg.setWidth(ebox.right - sbox.x -2);
13674
13675                 cg.on('click', _this.onMoreEventClick, _this, c.more);
13676                 
13677             }
13678             
13679         });
13680         
13681         
13682         
13683     },
13684     
13685     onEventEnter: function (e, el,event,d) {
13686         this.fireEvent('evententer', this, el, event);
13687     },
13688     
13689     onEventLeave: function (e, el,event,d) {
13690         this.fireEvent('eventleave', this, el, event);
13691     },
13692     
13693     onEventClick: function (e, el,event,d) {
13694         this.fireEvent('eventclick', this, el, event);
13695     },
13696     
13697     onMonthChange: function () {
13698         this.store.load();
13699     },
13700     
13701     onMoreEventClick: function(e, el, more)
13702     {
13703         var _this = this;
13704         
13705         this.calpopover.placement = 'right';
13706         this.calpopover.setTitle('More');
13707         
13708         this.calpopover.setContent('');
13709         
13710         var ctr = this.calpopover.el.select('.popover-content', true).first();
13711         
13712         Roo.each(more, function(m){
13713             var cfg = {
13714                 cls : 'fc-event-hori fc-event-draggable',
13715                 html : m.title
13716             }
13717             var cg = ctr.createChild(cfg);
13718             
13719             cg.on('click', _this.onEventClick, _this, m);
13720         });
13721         
13722         this.calpopover.show(el);
13723         
13724         
13725     },
13726     
13727     onLoad: function () 
13728     {   
13729         this.calevents = [];
13730         var cal = this;
13731         
13732         if(this.store.getCount() > 0){
13733             this.store.data.each(function(d){
13734                cal.addItem({
13735                     id : d.data.id,
13736                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
13737                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
13738                     time : d.data.start_time,
13739                     title : d.data.title,
13740                     description : d.data.description,
13741                     venue : d.data.venue
13742                 });
13743             });
13744         }
13745         
13746         this.renderEvents();
13747         
13748         if(this.calevents.length && this.loadMask){
13749             this.maskEl.hide();
13750         }
13751     },
13752     
13753     onBeforeLoad: function()
13754     {
13755         this.clearEvents();
13756         if(this.loadMask){
13757             this.maskEl.show();
13758         }
13759     }
13760 });
13761
13762  
13763  /*
13764  * - LGPL
13765  *
13766  * element
13767  * 
13768  */
13769
13770 /**
13771  * @class Roo.bootstrap.Popover
13772  * @extends Roo.bootstrap.Component
13773  * Bootstrap Popover class
13774  * @cfg {String} html contents of the popover   (or false to use children..)
13775  * @cfg {String} title of popover (or false to hide)
13776  * @cfg {String} placement how it is placed
13777  * @cfg {String} trigger click || hover (or false to trigger manually)
13778  * @cfg {String} over what (parent or false to trigger manually.)
13779  * @cfg {Number} delay - delay before showing
13780  
13781  * @constructor
13782  * Create a new Popover
13783  * @param {Object} config The config object
13784  */
13785
13786 Roo.bootstrap.Popover = function(config){
13787     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
13788 };
13789
13790 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
13791     
13792     title: 'Fill in a title',
13793     html: false,
13794     
13795     placement : 'right',
13796     trigger : 'hover', // hover
13797     
13798     delay : 0,
13799     
13800     over: 'parent',
13801     
13802     can_build_overlaid : false,
13803     
13804     getChildContainer : function()
13805     {
13806         return this.el.select('.popover-content',true).first();
13807     },
13808     
13809     getAutoCreate : function(){
13810          Roo.log('make popover?');
13811         var cfg = {
13812            cls : 'popover roo-dynamic',
13813            style: 'display:block',
13814            cn : [
13815                 {
13816                     cls : 'arrow'
13817                 },
13818                 {
13819                     cls : 'popover-inner',
13820                     cn : [
13821                         {
13822                             tag: 'h3',
13823                             cls: 'popover-title',
13824                             html : this.title
13825                         },
13826                         {
13827                             cls : 'popover-content',
13828                             html : this.html
13829                         }
13830                     ]
13831                     
13832                 }
13833            ]
13834         };
13835         
13836         return cfg;
13837     },
13838     setTitle: function(str)
13839     {
13840         this.el.select('.popover-title',true).first().dom.innerHTML = str;
13841     },
13842     setContent: function(str)
13843     {
13844         this.el.select('.popover-content',true).first().dom.innerHTML = str;
13845     },
13846     // as it get's added to the bottom of the page.
13847     onRender : function(ct, position)
13848     {
13849         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
13850         if(!this.el){
13851             var cfg = Roo.apply({},  this.getAutoCreate());
13852             cfg.id = Roo.id();
13853             
13854             if (this.cls) {
13855                 cfg.cls += ' ' + this.cls;
13856             }
13857             if (this.style) {
13858                 cfg.style = this.style;
13859             }
13860             Roo.log("adding to ")
13861             this.el = Roo.get(document.body).createChild(cfg, position);
13862             Roo.log(this.el);
13863         }
13864         this.initEvents();
13865     },
13866     
13867     initEvents : function()
13868     {
13869         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
13870         this.el.enableDisplayMode('block');
13871         this.el.hide();
13872         if (this.over === false) {
13873             return; 
13874         }
13875         if (this.triggers === false) {
13876             return;
13877         }
13878         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13879         var triggers = this.trigger ? this.trigger.split(' ') : [];
13880         Roo.each(triggers, function(trigger) {
13881         
13882             if (trigger == 'click') {
13883                 on_el.on('click', this.toggle, this);
13884             } else if (trigger != 'manual') {
13885                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
13886                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
13887       
13888                 on_el.on(eventIn  ,this.enter, this);
13889                 on_el.on(eventOut, this.leave, this);
13890             }
13891         }, this);
13892         
13893     },
13894     
13895     
13896     // private
13897     timeout : null,
13898     hoverState : null,
13899     
13900     toggle : function () {
13901         this.hoverState == 'in' ? this.leave() : this.enter();
13902     },
13903     
13904     enter : function () {
13905        
13906     
13907         clearTimeout(this.timeout);
13908     
13909         this.hoverState = 'in'
13910     
13911         if (!this.delay || !this.delay.show) {
13912             this.show();
13913             return 
13914         }
13915         var _t = this;
13916         this.timeout = setTimeout(function () {
13917             if (_t.hoverState == 'in') {
13918                 _t.show();
13919             }
13920         }, this.delay.show)
13921     },
13922     leave : function() {
13923         clearTimeout(this.timeout);
13924     
13925         this.hoverState = 'out'
13926     
13927         if (!this.delay || !this.delay.hide) {
13928             this.hide();
13929             return 
13930         }
13931         var _t = this;
13932         this.timeout = setTimeout(function () {
13933             if (_t.hoverState == 'out') {
13934                 _t.hide();
13935             }
13936         }, this.delay.hide)
13937     },
13938     
13939     show : function (on_el)
13940     {
13941         if (!on_el) {
13942             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13943         }
13944         // set content.
13945         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
13946         if (this.html !== false) {
13947             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
13948         }
13949         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
13950         if (!this.title.length) {
13951             this.el.select('.popover-title',true).hide();
13952         }
13953         
13954         var placement = typeof this.placement == 'function' ?
13955             this.placement.call(this, this.el, on_el) :
13956             this.placement;
13957             
13958         var autoToken = /\s?auto?\s?/i;
13959         var autoPlace = autoToken.test(placement);
13960         if (autoPlace) {
13961             placement = placement.replace(autoToken, '') || 'top';
13962         }
13963         
13964         //this.el.detach()
13965         //this.el.setXY([0,0]);
13966         this.el.show();
13967         this.el.dom.style.display='block';
13968         this.el.addClass(placement);
13969         
13970         //this.el.appendTo(on_el);
13971         
13972         var p = this.getPosition();
13973         var box = this.el.getBox();
13974         
13975         if (autoPlace) {
13976             // fixme..
13977         }
13978         var align = Roo.bootstrap.Popover.alignment[placement]
13979         this.el.alignTo(on_el, align[0],align[1]);
13980         //var arrow = this.el.select('.arrow',true).first();
13981         //arrow.set(align[2], 
13982         
13983         this.el.addClass('in');
13984         this.hoverState = null;
13985         
13986         if (this.el.hasClass('fade')) {
13987             // fade it?
13988         }
13989         
13990     },
13991     hide : function()
13992     {
13993         this.el.setXY([0,0]);
13994         this.el.removeClass('in');
13995         this.el.hide();
13996         
13997     }
13998     
13999 });
14000
14001 Roo.bootstrap.Popover.alignment = {
14002     'left' : ['r-l', [-10,0], 'right'],
14003     'right' : ['l-r', [10,0], 'left'],
14004     'bottom' : ['t-b', [0,10], 'top'],
14005     'top' : [ 'b-t', [0,-10], 'bottom']
14006 };
14007
14008  /*
14009  * - LGPL
14010  *
14011  * Progress
14012  * 
14013  */
14014
14015 /**
14016  * @class Roo.bootstrap.Progress
14017  * @extends Roo.bootstrap.Component
14018  * Bootstrap Progress class
14019  * @cfg {Boolean} striped striped of the progress bar
14020  * @cfg {Boolean} active animated of the progress bar
14021  * 
14022  * 
14023  * @constructor
14024  * Create a new Progress
14025  * @param {Object} config The config object
14026  */
14027
14028 Roo.bootstrap.Progress = function(config){
14029     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
14030 };
14031
14032 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
14033     
14034     striped : false,
14035     active: false,
14036     
14037     getAutoCreate : function(){
14038         var cfg = {
14039             tag: 'div',
14040             cls: 'progress'
14041         };
14042         
14043         
14044         if(this.striped){
14045             cfg.cls += ' progress-striped';
14046         }
14047       
14048         if(this.active){
14049             cfg.cls += ' active';
14050         }
14051         
14052         
14053         return cfg;
14054     }
14055    
14056 });
14057
14058  
14059
14060  /*
14061  * - LGPL
14062  *
14063  * ProgressBar
14064  * 
14065  */
14066
14067 /**
14068  * @class Roo.bootstrap.ProgressBar
14069  * @extends Roo.bootstrap.Component
14070  * Bootstrap ProgressBar class
14071  * @cfg {Number} aria_valuenow aria-value now
14072  * @cfg {Number} aria_valuemin aria-value min
14073  * @cfg {Number} aria_valuemax aria-value max
14074  * @cfg {String} label label for the progress bar
14075  * @cfg {String} panel (success | info | warning | danger )
14076  * @cfg {String} role role of the progress bar
14077  * @cfg {String} sr_only text
14078  * 
14079  * 
14080  * @constructor
14081  * Create a new ProgressBar
14082  * @param {Object} config The config object
14083  */
14084
14085 Roo.bootstrap.ProgressBar = function(config){
14086     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
14087 };
14088
14089 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
14090     
14091     aria_valuenow : 0,
14092     aria_valuemin : 0,
14093     aria_valuemax : 100,
14094     label : false,
14095     panel : false,
14096     role : false,
14097     sr_only: false,
14098     
14099     getAutoCreate : function()
14100     {
14101         
14102         var cfg = {
14103             tag: 'div',
14104             cls: 'progress-bar',
14105             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
14106         };
14107         
14108         if(this.sr_only){
14109             cfg.cn = {
14110                 tag: 'span',
14111                 cls: 'sr-only',
14112                 html: this.sr_only
14113             }
14114         }
14115         
14116         if(this.role){
14117             cfg.role = this.role;
14118         }
14119         
14120         if(this.aria_valuenow){
14121             cfg['aria-valuenow'] = this.aria_valuenow;
14122         }
14123         
14124         if(this.aria_valuemin){
14125             cfg['aria-valuemin'] = this.aria_valuemin;
14126         }
14127         
14128         if(this.aria_valuemax){
14129             cfg['aria-valuemax'] = this.aria_valuemax;
14130         }
14131         
14132         if(this.label && !this.sr_only){
14133             cfg.html = this.label;
14134         }
14135         
14136         if(this.panel){
14137             cfg.cls += ' progress-bar-' + this.panel;
14138         }
14139         
14140         return cfg;
14141     },
14142     
14143     update : function(aria_valuenow)
14144     {
14145         this.aria_valuenow = aria_valuenow;
14146         
14147         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
14148     }
14149    
14150 });
14151
14152  
14153
14154  /*
14155  * - LGPL
14156  *
14157  * column
14158  * 
14159  */
14160
14161 /**
14162  * @class Roo.bootstrap.TabGroup
14163  * @extends Roo.bootstrap.Column
14164  * Bootstrap Column class
14165  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
14166  * @cfg {Boolean} carousel true to make the group behave like a carousel
14167  * 
14168  * @constructor
14169  * Create a new TabGroup
14170  * @param {Object} config The config object
14171  */
14172
14173 Roo.bootstrap.TabGroup = function(config){
14174     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
14175     if (!this.navId) {
14176         this.navId = Roo.id();
14177     }
14178     this.tabs = [];
14179     Roo.bootstrap.TabGroup.register(this);
14180     
14181 };
14182
14183 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
14184     
14185     carousel : false,
14186     transition : false,
14187      
14188     getAutoCreate : function()
14189     {
14190         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
14191         
14192         cfg.cls += ' tab-content';
14193         
14194         if (this.carousel) {
14195             cfg.cls += ' carousel slide';
14196             cfg.cn = [{
14197                cls : 'carousel-inner'
14198             }]
14199         }
14200         
14201         
14202         return cfg;
14203     },
14204     getChildContainer : function()
14205     {
14206         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
14207     },
14208     
14209     /**
14210     * register a Navigation item
14211     * @param {Roo.bootstrap.NavItem} the navitem to add
14212     */
14213     register : function(item)
14214     {
14215         this.tabs.push( item);
14216         item.navId = this.navId; // not really needed..
14217     
14218     },
14219     
14220     getActivePanel : function()
14221     {
14222         var r = false;
14223         Roo.each(this.tabs, function(t) {
14224             if (t.active) {
14225                 r = t;
14226                 return false;
14227             }
14228             return null;
14229         });
14230         return r;
14231         
14232     },
14233     getPanelByName : function(n)
14234     {
14235         var r = false;
14236         Roo.each(this.tabs, function(t) {
14237             if (t.tabId == n) {
14238                 r = t;
14239                 return false;
14240             }
14241             return null;
14242         });
14243         return r;
14244     },
14245     indexOfPanel : function(p)
14246     {
14247         var r = false;
14248         Roo.each(this.tabs, function(t,i) {
14249             if (t.tabId == p.tabId) {
14250                 r = i;
14251                 return false;
14252             }
14253             return null;
14254         });
14255         return r;
14256     },
14257     /**
14258      * show a specific panel
14259      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
14260      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
14261      */
14262     showPanel : function (pan)
14263     {
14264         
14265         if (typeof(pan) == 'number') {
14266             pan = this.tabs[pan];
14267         }
14268         if (typeof(pan) == 'string') {
14269             pan = this.getPanelByName(pan);
14270         }
14271         if (pan.tabId == this.getActivePanel().tabId) {
14272             return true;
14273         }
14274         var cur = this.getActivePanel();
14275         
14276         if (false === cur.fireEvent('beforedeactivate')) {
14277             return false;
14278         }
14279         
14280         if (this.carousel) {
14281             this.transition = true;
14282             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
14283             var lr = dir == 'next' ? 'left' : 'right';
14284             pan.el.addClass(dir); // or prev
14285             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
14286             cur.el.addClass(lr); // or right
14287             pan.el.addClass(lr);
14288             
14289             var _this = this;
14290             cur.el.on('transitionend', function() {
14291                 Roo.log("trans end?");
14292                 
14293                 pan.el.removeClass([lr,dir]);
14294                 pan.setActive(true);
14295                 
14296                 cur.el.removeClass([lr]);
14297                 cur.setActive(false);
14298                 
14299                 _this.transition = false;
14300                 
14301             }, this, { single:  true } );
14302             return true;
14303         }
14304         
14305         cur.setActive(false);
14306         pan.setActive(true);
14307         return true;
14308         
14309     },
14310     showPanelNext : function()
14311     {
14312         var i = this.indexOfPanel(this.getActivePanel());
14313         if (i > this.tabs.length) {
14314             return;
14315         }
14316         this.showPanel(this.tabs[i+1]);
14317     },
14318     showPanelPrev : function()
14319     {
14320         var i = this.indexOfPanel(this.getActivePanel());
14321         if (i  < 1) {
14322             return;
14323         }
14324         this.showPanel(this.tabs[i-1]);
14325     }
14326     
14327     
14328   
14329 });
14330
14331  
14332
14333  
14334  
14335 Roo.apply(Roo.bootstrap.TabGroup, {
14336     
14337     groups: {},
14338      /**
14339     * register a Navigation Group
14340     * @param {Roo.bootstrap.NavGroup} the navgroup to add
14341     */
14342     register : function(navgrp)
14343     {
14344         this.groups[navgrp.navId] = navgrp;
14345         
14346     },
14347     /**
14348     * fetch a Navigation Group based on the navigation ID
14349     * if one does not exist , it will get created.
14350     * @param {string} the navgroup to add
14351     * @returns {Roo.bootstrap.NavGroup} the navgroup 
14352     */
14353     get: function(navId) {
14354         if (typeof(this.groups[navId]) == 'undefined') {
14355             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
14356         }
14357         return this.groups[navId] ;
14358     }
14359     
14360     
14361     
14362 });
14363
14364  /*
14365  * - LGPL
14366  *
14367  * TabPanel
14368  * 
14369  */
14370
14371 /**
14372  * @class Roo.bootstrap.TabPanel
14373  * @extends Roo.bootstrap.Component
14374  * Bootstrap TabPanel class
14375  * @cfg {Boolean} active panel active
14376  * @cfg {String} html panel content
14377  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
14378  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
14379  * 
14380  * 
14381  * @constructor
14382  * Create a new TabPanel
14383  * @param {Object} config The config object
14384  */
14385
14386 Roo.bootstrap.TabPanel = function(config){
14387     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
14388     this.addEvents({
14389         /**
14390              * @event changed
14391              * Fires when the active status changes
14392              * @param {Roo.bootstrap.TabPanel} this
14393              * @param {Boolean} state the new state
14394             
14395          */
14396         'changed': true,
14397         /**
14398              * @event beforedeactivate
14399              * Fires before a tab is de-activated - can be used to do validation on a form.
14400              * @param {Roo.bootstrap.TabPanel} this
14401              * @return {Boolean} false if there is an error
14402             
14403          */
14404         'beforedeactivate': true
14405      });
14406     
14407     this.tabId = this.tabId || Roo.id();
14408   
14409 };
14410
14411 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
14412     
14413     active: false,
14414     html: false,
14415     tabId: false,
14416     navId : false,
14417     
14418     getAutoCreate : function(){
14419         var cfg = {
14420             tag: 'div',
14421             // item is needed for carousel - not sure if it has any effect otherwise
14422             cls: 'tab-pane item',
14423             html: this.html || ''
14424         };
14425         
14426         if(this.active){
14427             cfg.cls += ' active';
14428         }
14429         
14430         if(this.tabId){
14431             cfg.tabId = this.tabId;
14432         }
14433         
14434         
14435         return cfg;
14436     },
14437     
14438     initEvents:  function()
14439     {
14440         Roo.log('-------- init events on tab panel ---------');
14441         
14442         var p = this.parent();
14443         this.navId = this.navId || p.navId;
14444         
14445         if (typeof(this.navId) != 'undefined') {
14446             // not really needed.. but just in case.. parent should be a NavGroup.
14447             var tg = Roo.bootstrap.TabGroup.get(this.navId);
14448             Roo.log(['register', tg, this]);
14449             tg.register(this);
14450         }
14451     },
14452     
14453     
14454     onRender : function(ct, position)
14455     {
14456        // Roo.log("Call onRender: " + this.xtype);
14457         
14458         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
14459         
14460         
14461         
14462         
14463         
14464     },
14465     
14466     setActive: function(state)
14467     {
14468         Roo.log("panel - set active " + this.tabId + "=" + state);
14469         
14470         this.active = state;
14471         if (!state) {
14472             this.el.removeClass('active');
14473             
14474         } else  if (!this.el.hasClass('active')) {
14475             this.el.addClass('active');
14476         }
14477         this.fireEvent('changed', this, state);
14478     }
14479     
14480     
14481 });
14482  
14483
14484  
14485
14486  /*
14487  * - LGPL
14488  *
14489  * DateField
14490  * 
14491  */
14492
14493 /**
14494  * @class Roo.bootstrap.DateField
14495  * @extends Roo.bootstrap.Input
14496  * Bootstrap DateField class
14497  * @cfg {Number} weekStart default 0
14498  * @cfg {String} viewMode default empty, (months|years)
14499  * @cfg {String} minViewMode default empty, (months|years)
14500  * @cfg {Number} startDate default -Infinity
14501  * @cfg {Number} endDate default Infinity
14502  * @cfg {Boolean} todayHighlight default false
14503  * @cfg {Boolean} todayBtn default false
14504  * @cfg {Boolean} calendarWeeks default false
14505  * @cfg {Object} daysOfWeekDisabled default empty
14506  * @cfg {Boolean} singleMode default false (true | false)
14507  * 
14508  * @cfg {Boolean} keyboardNavigation default true
14509  * @cfg {String} language default en
14510  * 
14511  * @constructor
14512  * Create a new DateField
14513  * @param {Object} config The config object
14514  */
14515
14516 Roo.bootstrap.DateField = function(config){
14517     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
14518      this.addEvents({
14519             /**
14520              * @event show
14521              * Fires when this field show.
14522              * @param {Roo.bootstrap.DateField} this
14523              * @param {Mixed} date The date value
14524              */
14525             show : true,
14526             /**
14527              * @event show
14528              * Fires when this field hide.
14529              * @param {Roo.bootstrap.DateField} this
14530              * @param {Mixed} date The date value
14531              */
14532             hide : true,
14533             /**
14534              * @event select
14535              * Fires when select a date.
14536              * @param {Roo.bootstrap.DateField} this
14537              * @param {Mixed} date The date value
14538              */
14539             select : true
14540         });
14541 };
14542
14543 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
14544     
14545     /**
14546      * @cfg {String} format
14547      * The default date format string which can be overriden for localization support.  The format must be
14548      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
14549      */
14550     format : "m/d/y",
14551     /**
14552      * @cfg {String} altFormats
14553      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
14554      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
14555      */
14556     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
14557     
14558     weekStart : 0,
14559     
14560     viewMode : '',
14561     
14562     minViewMode : '',
14563     
14564     todayHighlight : false,
14565     
14566     todayBtn: false,
14567     
14568     language: 'en',
14569     
14570     keyboardNavigation: true,
14571     
14572     calendarWeeks: false,
14573     
14574     startDate: -Infinity,
14575     
14576     endDate: Infinity,
14577     
14578     daysOfWeekDisabled: [],
14579     
14580     _events: [],
14581     
14582     singleMode : false,
14583     
14584     UTCDate: function()
14585     {
14586         return new Date(Date.UTC.apply(Date, arguments));
14587     },
14588     
14589     UTCToday: function()
14590     {
14591         var today = new Date();
14592         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
14593     },
14594     
14595     getDate: function() {
14596             var d = this.getUTCDate();
14597             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
14598     },
14599     
14600     getUTCDate: function() {
14601             return this.date;
14602     },
14603     
14604     setDate: function(d) {
14605             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
14606     },
14607     
14608     setUTCDate: function(d) {
14609             this.date = d;
14610             this.setValue(this.formatDate(this.date));
14611     },
14612         
14613     onRender: function(ct, position)
14614     {
14615         
14616         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
14617         
14618         this.language = this.language || 'en';
14619         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
14620         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
14621         
14622         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
14623         this.format = this.format || 'm/d/y';
14624         this.isInline = false;
14625         this.isInput = true;
14626         this.component = this.el.select('.add-on', true).first() || false;
14627         this.component = (this.component && this.component.length === 0) ? false : this.component;
14628         this.hasInput = this.component && this.inputEL().length;
14629         
14630         if (typeof(this.minViewMode === 'string')) {
14631             switch (this.minViewMode) {
14632                 case 'months':
14633                     this.minViewMode = 1;
14634                     break;
14635                 case 'years':
14636                     this.minViewMode = 2;
14637                     break;
14638                 default:
14639                     this.minViewMode = 0;
14640                     break;
14641             }
14642         }
14643         
14644         if (typeof(this.viewMode === 'string')) {
14645             switch (this.viewMode) {
14646                 case 'months':
14647                     this.viewMode = 1;
14648                     break;
14649                 case 'years':
14650                     this.viewMode = 2;
14651                     break;
14652                 default:
14653                     this.viewMode = 0;
14654                     break;
14655             }
14656         }
14657                 
14658         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
14659         
14660 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
14661         
14662         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14663         
14664         this.picker().on('mousedown', this.onMousedown, this);
14665         this.picker().on('click', this.onClick, this);
14666         
14667         this.picker().addClass('datepicker-dropdown');
14668         
14669         this.startViewMode = this.viewMode;
14670         
14671         if(this.singleMode){
14672             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
14673                 v.setVisibilityMode(Roo.Element.DISPLAY)
14674                 v.hide();
14675             })
14676             
14677             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
14678                 v.setStyle('width', '189px');
14679             });
14680         }
14681         
14682         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
14683             if(!this.calendarWeeks){
14684                 v.remove();
14685                 return;
14686             };
14687             
14688             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
14689             v.attr('colspan', function(i, val){
14690                 return parseInt(val) + 1;
14691             });
14692         })
14693                         
14694         
14695         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
14696         
14697         this.setStartDate(this.startDate);
14698         this.setEndDate(this.endDate);
14699         
14700         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
14701         
14702         this.fillDow();
14703         this.fillMonths();
14704         this.update();
14705         this.showMode();
14706         
14707         if(this.isInline) {
14708             this.show();
14709         }
14710     },
14711     
14712     picker : function()
14713     {
14714         return this.pickerEl;
14715 //        return this.el.select('.datepicker', true).first();
14716     },
14717     
14718     fillDow: function()
14719     {
14720         var dowCnt = this.weekStart;
14721         
14722         var dow = {
14723             tag: 'tr',
14724             cn: [
14725                 
14726             ]
14727         };
14728         
14729         if(this.calendarWeeks){
14730             dow.cn.push({
14731                 tag: 'th',
14732                 cls: 'cw',
14733                 html: '&nbsp;'
14734             })
14735         }
14736         
14737         while (dowCnt < this.weekStart + 7) {
14738             dow.cn.push({
14739                 tag: 'th',
14740                 cls: 'dow',
14741                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
14742             });
14743         }
14744         
14745         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
14746     },
14747     
14748     fillMonths: function()
14749     {    
14750         var i = 0
14751         var months = this.picker().select('>.datepicker-months td', true).first();
14752         
14753         months.dom.innerHTML = '';
14754         
14755         while (i < 12) {
14756             var month = {
14757                 tag: 'span',
14758                 cls: 'month',
14759                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
14760             }
14761             
14762             months.createChild(month);
14763         }
14764         
14765     },
14766     
14767     update: function()
14768     {
14769         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;
14770         
14771         if (this.date < this.startDate) {
14772             this.viewDate = new Date(this.startDate);
14773         } else if (this.date > this.endDate) {
14774             this.viewDate = new Date(this.endDate);
14775         } else {
14776             this.viewDate = new Date(this.date);
14777         }
14778         
14779         this.fill();
14780     },
14781     
14782     fill: function() 
14783     {
14784         var d = new Date(this.viewDate),
14785                 year = d.getUTCFullYear(),
14786                 month = d.getUTCMonth(),
14787                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
14788                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
14789                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
14790                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
14791                 currentDate = this.date && this.date.valueOf(),
14792                 today = this.UTCToday();
14793         
14794         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
14795         
14796 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
14797         
14798 //        this.picker.select('>tfoot th.today').
14799 //                                              .text(dates[this.language].today)
14800 //                                              .toggle(this.todayBtn !== false);
14801     
14802         this.updateNavArrows();
14803         this.fillMonths();
14804                                                 
14805         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
14806         
14807         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
14808          
14809         prevMonth.setUTCDate(day);
14810         
14811         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
14812         
14813         var nextMonth = new Date(prevMonth);
14814         
14815         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
14816         
14817         nextMonth = nextMonth.valueOf();
14818         
14819         var fillMonths = false;
14820         
14821         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
14822         
14823         while(prevMonth.valueOf() < nextMonth) {
14824             var clsName = '';
14825             
14826             if (prevMonth.getUTCDay() === this.weekStart) {
14827                 if(fillMonths){
14828                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
14829                 }
14830                     
14831                 fillMonths = {
14832                     tag: 'tr',
14833                     cn: []
14834                 };
14835                 
14836                 if(this.calendarWeeks){
14837                     // ISO 8601: First week contains first thursday.
14838                     // ISO also states week starts on Monday, but we can be more abstract here.
14839                     var
14840                     // Start of current week: based on weekstart/current date
14841                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
14842                     // Thursday of this week
14843                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
14844                     // First Thursday of year, year from thursday
14845                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
14846                     // Calendar week: ms between thursdays, div ms per day, div 7 days
14847                     calWeek =  (th - yth) / 864e5 / 7 + 1;
14848                     
14849                     fillMonths.cn.push({
14850                         tag: 'td',
14851                         cls: 'cw',
14852                         html: calWeek
14853                     });
14854                 }
14855             }
14856             
14857             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
14858                 clsName += ' old';
14859             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
14860                 clsName += ' new';
14861             }
14862             if (this.todayHighlight &&
14863                 prevMonth.getUTCFullYear() == today.getFullYear() &&
14864                 prevMonth.getUTCMonth() == today.getMonth() &&
14865                 prevMonth.getUTCDate() == today.getDate()) {
14866                 clsName += ' today';
14867             }
14868             
14869             if (currentDate && prevMonth.valueOf() === currentDate) {
14870                 clsName += ' active';
14871             }
14872             
14873             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
14874                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
14875                     clsName += ' disabled';
14876             }
14877             
14878             fillMonths.cn.push({
14879                 tag: 'td',
14880                 cls: 'day ' + clsName,
14881                 html: prevMonth.getDate()
14882             })
14883             
14884             prevMonth.setDate(prevMonth.getDate()+1);
14885         }
14886           
14887         var currentYear = this.date && this.date.getUTCFullYear();
14888         var currentMonth = this.date && this.date.getUTCMonth();
14889         
14890         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
14891         
14892         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
14893             v.removeClass('active');
14894             
14895             if(currentYear === year && k === currentMonth){
14896                 v.addClass('active');
14897             }
14898             
14899             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
14900                 v.addClass('disabled');
14901             }
14902             
14903         });
14904         
14905         
14906         year = parseInt(year/10, 10) * 10;
14907         
14908         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
14909         
14910         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
14911         
14912         year -= 1;
14913         for (var i = -1; i < 11; i++) {
14914             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
14915                 tag: 'span',
14916                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
14917                 html: year
14918             })
14919             
14920             year += 1;
14921         }
14922     },
14923     
14924     showMode: function(dir) 
14925     {
14926         if (dir) {
14927             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
14928         }
14929         
14930         Roo.each(this.picker().select('>div',true).elements, function(v){
14931             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14932             v.hide();
14933         });
14934         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
14935     },
14936     
14937     place: function()
14938     {
14939         if(this.isInline) return;
14940         
14941         this.picker().removeClass(['bottom', 'top']);
14942         
14943         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
14944             /*
14945              * place to the top of element!
14946              *
14947              */
14948             
14949             this.picker().addClass('top');
14950             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
14951             
14952             return;
14953         }
14954         
14955         this.picker().addClass('bottom');
14956         
14957         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
14958     },
14959     
14960     parseDate : function(value)
14961     {
14962         if(!value || value instanceof Date){
14963             return value;
14964         }
14965         var v = Date.parseDate(value, this.format);
14966         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
14967             v = Date.parseDate(value, 'Y-m-d');
14968         }
14969         if(!v && this.altFormats){
14970             if(!this.altFormatsArray){
14971                 this.altFormatsArray = this.altFormats.split("|");
14972             }
14973             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
14974                 v = Date.parseDate(value, this.altFormatsArray[i]);
14975             }
14976         }
14977         return v;
14978     },
14979     
14980     formatDate : function(date, fmt)
14981     {   
14982         return (!date || !(date instanceof Date)) ?
14983         date : date.dateFormat(fmt || this.format);
14984     },
14985     
14986     onFocus : function()
14987     {
14988         Roo.bootstrap.DateField.superclass.onFocus.call(this);
14989         this.show();
14990     },
14991     
14992     onBlur : function()
14993     {
14994         Roo.bootstrap.DateField.superclass.onBlur.call(this);
14995         
14996         var d = this.inputEl().getValue();
14997         
14998         this.setValue(d);
14999                 
15000         this.hide();
15001     },
15002     
15003     show : function()
15004     {
15005         this.picker().show();
15006         this.update();
15007         this.place();
15008         
15009         this.fireEvent('show', this, this.date);
15010     },
15011     
15012     hide : function()
15013     {
15014         if(this.isInline) return;
15015         this.picker().hide();
15016         this.viewMode = this.startViewMode;
15017         this.showMode();
15018         
15019         this.fireEvent('hide', this, this.date);
15020         
15021     },
15022     
15023     onMousedown: function(e)
15024     {
15025         e.stopPropagation();
15026         e.preventDefault();
15027     },
15028     
15029     keyup: function(e)
15030     {
15031         Roo.bootstrap.DateField.superclass.keyup.call(this);
15032         this.update();
15033     },
15034
15035     setValue: function(v)
15036     {
15037         
15038         // v can be a string or a date..
15039         
15040         
15041         var d = new Date(this.parseDate(v) ).clearTime();
15042         
15043         if(isNaN(d.getTime())){
15044             this.date = this.viewDate = '';
15045             Roo.bootstrap.DateField.superclass.setValue.call(this, '');
15046             return;
15047         }
15048         
15049         v = this.formatDate(d);
15050         
15051         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
15052         
15053         this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
15054      
15055         this.update();
15056
15057         this.fireEvent('select', this, this.date);
15058         
15059     },
15060     
15061     getValue: function()
15062     {
15063         return this.formatDate(this.date);
15064     },
15065     
15066     fireKey: function(e)
15067     {
15068         if (!this.picker().isVisible()){
15069             if (e.keyCode == 27) // allow escape to hide and re-show picker
15070                 this.show();
15071             return;
15072         }
15073         
15074         var dateChanged = false,
15075         dir, day, month,
15076         newDate, newViewDate;
15077         
15078         switch(e.keyCode){
15079             case 27: // escape
15080                 this.hide();
15081                 e.preventDefault();
15082                 break;
15083             case 37: // left
15084             case 39: // right
15085                 if (!this.keyboardNavigation) break;
15086                 dir = e.keyCode == 37 ? -1 : 1;
15087                 
15088                 if (e.ctrlKey){
15089                     newDate = this.moveYear(this.date, dir);
15090                     newViewDate = this.moveYear(this.viewDate, dir);
15091                 } else if (e.shiftKey){
15092                     newDate = this.moveMonth(this.date, dir);
15093                     newViewDate = this.moveMonth(this.viewDate, dir);
15094                 } else {
15095                     newDate = new Date(this.date);
15096                     newDate.setUTCDate(this.date.getUTCDate() + dir);
15097                     newViewDate = new Date(this.viewDate);
15098                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
15099                 }
15100                 if (this.dateWithinRange(newDate)){
15101                     this.date = newDate;
15102                     this.viewDate = newViewDate;
15103                     this.setValue(this.formatDate(this.date));
15104 //                    this.update();
15105                     e.preventDefault();
15106                     dateChanged = true;
15107                 }
15108                 break;
15109             case 38: // up
15110             case 40: // down
15111                 if (!this.keyboardNavigation) break;
15112                 dir = e.keyCode == 38 ? -1 : 1;
15113                 if (e.ctrlKey){
15114                     newDate = this.moveYear(this.date, dir);
15115                     newViewDate = this.moveYear(this.viewDate, dir);
15116                 } else if (e.shiftKey){
15117                     newDate = this.moveMonth(this.date, dir);
15118                     newViewDate = this.moveMonth(this.viewDate, dir);
15119                 } else {
15120                     newDate = new Date(this.date);
15121                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
15122                     newViewDate = new Date(this.viewDate);
15123                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
15124                 }
15125                 if (this.dateWithinRange(newDate)){
15126                     this.date = newDate;
15127                     this.viewDate = newViewDate;
15128                     this.setValue(this.formatDate(this.date));
15129 //                    this.update();
15130                     e.preventDefault();
15131                     dateChanged = true;
15132                 }
15133                 break;
15134             case 13: // enter
15135                 this.setValue(this.formatDate(this.date));
15136                 this.hide();
15137                 e.preventDefault();
15138                 break;
15139             case 9: // tab
15140                 this.setValue(this.formatDate(this.date));
15141                 this.hide();
15142                 break;
15143             case 16: // shift
15144             case 17: // ctrl
15145             case 18: // alt
15146                 break;
15147             default :
15148                 this.hide();
15149                 
15150         }
15151     },
15152     
15153     
15154     onClick: function(e) 
15155     {
15156         e.stopPropagation();
15157         e.preventDefault();
15158         
15159         var target = e.getTarget();
15160         
15161         if(target.nodeName.toLowerCase() === 'i'){
15162             target = Roo.get(target).dom.parentNode;
15163         }
15164         
15165         var nodeName = target.nodeName;
15166         var className = target.className;
15167         var html = target.innerHTML;
15168         //Roo.log(nodeName);
15169         
15170         switch(nodeName.toLowerCase()) {
15171             case 'th':
15172                 switch(className) {
15173                     case 'switch':
15174                         this.showMode(1);
15175                         break;
15176                     case 'prev':
15177                     case 'next':
15178                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
15179                         switch(this.viewMode){
15180                                 case 0:
15181                                         this.viewDate = this.moveMonth(this.viewDate, dir);
15182                                         break;
15183                                 case 1:
15184                                 case 2:
15185                                         this.viewDate = this.moveYear(this.viewDate, dir);
15186                                         break;
15187                         }
15188                         this.fill();
15189                         break;
15190                     case 'today':
15191                         var date = new Date();
15192                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
15193 //                        this.fill()
15194                         this.setValue(this.formatDate(this.date));
15195                         
15196                         this.hide();
15197                         break;
15198                 }
15199                 break;
15200             case 'span':
15201                 if (className.indexOf('disabled') < 0) {
15202                     this.viewDate.setUTCDate(1);
15203                     if (className.indexOf('month') > -1) {
15204                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
15205                     } else {
15206                         var year = parseInt(html, 10) || 0;
15207                         this.viewDate.setUTCFullYear(year);
15208                         
15209                     }
15210                     
15211                     if(this.singleMode){
15212                         this.setValue(this.formatDate(this.viewDate));
15213                         this.hide();
15214                         return;
15215                     }
15216                     
15217                     this.showMode(-1);
15218                     this.fill();
15219                 }
15220                 break;
15221                 
15222             case 'td':
15223                 //Roo.log(className);
15224                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
15225                     var day = parseInt(html, 10) || 1;
15226                     var year = this.viewDate.getUTCFullYear(),
15227                         month = this.viewDate.getUTCMonth();
15228
15229                     if (className.indexOf('old') > -1) {
15230                         if(month === 0 ){
15231                             month = 11;
15232                             year -= 1;
15233                         }else{
15234                             month -= 1;
15235                         }
15236                     } else if (className.indexOf('new') > -1) {
15237                         if (month == 11) {
15238                             month = 0;
15239                             year += 1;
15240                         } else {
15241                             month += 1;
15242                         }
15243                     }
15244                     //Roo.log([year,month,day]);
15245                     this.date = this.UTCDate(year, month, day,0,0,0,0);
15246                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
15247 //                    this.fill();
15248                     //Roo.log(this.formatDate(this.date));
15249                     this.setValue(this.formatDate(this.date));
15250                     this.hide();
15251                 }
15252                 break;
15253         }
15254     },
15255     
15256     setStartDate: function(startDate)
15257     {
15258         this.startDate = startDate || -Infinity;
15259         if (this.startDate !== -Infinity) {
15260             this.startDate = this.parseDate(this.startDate);
15261         }
15262         this.update();
15263         this.updateNavArrows();
15264     },
15265
15266     setEndDate: function(endDate)
15267     {
15268         this.endDate = endDate || Infinity;
15269         if (this.endDate !== Infinity) {
15270             this.endDate = this.parseDate(this.endDate);
15271         }
15272         this.update();
15273         this.updateNavArrows();
15274     },
15275     
15276     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
15277     {
15278         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
15279         if (typeof(this.daysOfWeekDisabled) !== 'object') {
15280             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
15281         }
15282         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
15283             return parseInt(d, 10);
15284         });
15285         this.update();
15286         this.updateNavArrows();
15287     },
15288     
15289     updateNavArrows: function() 
15290     {
15291         if(this.singleMode){
15292             return;
15293         }
15294         
15295         var d = new Date(this.viewDate),
15296         year = d.getUTCFullYear(),
15297         month = d.getUTCMonth();
15298         
15299         Roo.each(this.picker().select('.prev', true).elements, function(v){
15300             v.show();
15301             switch (this.viewMode) {
15302                 case 0:
15303
15304                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
15305                         v.hide();
15306                     }
15307                     break;
15308                 case 1:
15309                 case 2:
15310                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
15311                         v.hide();
15312                     }
15313                     break;
15314             }
15315         });
15316         
15317         Roo.each(this.picker().select('.next', true).elements, function(v){
15318             v.show();
15319             switch (this.viewMode) {
15320                 case 0:
15321
15322                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
15323                         v.hide();
15324                     }
15325                     break;
15326                 case 1:
15327                 case 2:
15328                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
15329                         v.hide();
15330                     }
15331                     break;
15332             }
15333         })
15334     },
15335     
15336     moveMonth: function(date, dir)
15337     {
15338         if (!dir) return date;
15339         var new_date = new Date(date.valueOf()),
15340         day = new_date.getUTCDate(),
15341         month = new_date.getUTCMonth(),
15342         mag = Math.abs(dir),
15343         new_month, test;
15344         dir = dir > 0 ? 1 : -1;
15345         if (mag == 1){
15346             test = dir == -1
15347             // If going back one month, make sure month is not current month
15348             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
15349             ? function(){
15350                 return new_date.getUTCMonth() == month;
15351             }
15352             // If going forward one month, make sure month is as expected
15353             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
15354             : function(){
15355                 return new_date.getUTCMonth() != new_month;
15356             };
15357             new_month = month + dir;
15358             new_date.setUTCMonth(new_month);
15359             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
15360             if (new_month < 0 || new_month > 11)
15361                 new_month = (new_month + 12) % 12;
15362         } else {
15363             // For magnitudes >1, move one month at a time...
15364             for (var i=0; i<mag; i++)
15365                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
15366                 new_date = this.moveMonth(new_date, dir);
15367             // ...then reset the day, keeping it in the new month
15368             new_month = new_date.getUTCMonth();
15369             new_date.setUTCDate(day);
15370             test = function(){
15371                 return new_month != new_date.getUTCMonth();
15372             };
15373         }
15374         // Common date-resetting loop -- if date is beyond end of month, make it
15375         // end of month
15376         while (test()){
15377             new_date.setUTCDate(--day);
15378             new_date.setUTCMonth(new_month);
15379         }
15380         return new_date;
15381     },
15382
15383     moveYear: function(date, dir)
15384     {
15385         return this.moveMonth(date, dir*12);
15386     },
15387
15388     dateWithinRange: function(date)
15389     {
15390         return date >= this.startDate && date <= this.endDate;
15391     },
15392
15393     
15394     remove: function() 
15395     {
15396         this.picker().remove();
15397     }
15398    
15399 });
15400
15401 Roo.apply(Roo.bootstrap.DateField,  {
15402     
15403     head : {
15404         tag: 'thead',
15405         cn: [
15406         {
15407             tag: 'tr',
15408             cn: [
15409             {
15410                 tag: 'th',
15411                 cls: 'prev',
15412                 html: '<i class="fa fa-arrow-left"/>'
15413             },
15414             {
15415                 tag: 'th',
15416                 cls: 'switch',
15417                 colspan: '5'
15418             },
15419             {
15420                 tag: 'th',
15421                 cls: 'next',
15422                 html: '<i class="fa fa-arrow-right"/>'
15423             }
15424
15425             ]
15426         }
15427         ]
15428     },
15429     
15430     content : {
15431         tag: 'tbody',
15432         cn: [
15433         {
15434             tag: 'tr',
15435             cn: [
15436             {
15437                 tag: 'td',
15438                 colspan: '7'
15439             }
15440             ]
15441         }
15442         ]
15443     },
15444     
15445     footer : {
15446         tag: 'tfoot',
15447         cn: [
15448         {
15449             tag: 'tr',
15450             cn: [
15451             {
15452                 tag: 'th',
15453                 colspan: '7',
15454                 cls: 'today'
15455             }
15456                     
15457             ]
15458         }
15459         ]
15460     },
15461     
15462     dates:{
15463         en: {
15464             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
15465             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
15466             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
15467             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
15468             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
15469             today: "Today"
15470         }
15471     },
15472     
15473     modes: [
15474     {
15475         clsName: 'days',
15476         navFnc: 'Month',
15477         navStep: 1
15478     },
15479     {
15480         clsName: 'months',
15481         navFnc: 'FullYear',
15482         navStep: 1
15483     },
15484     {
15485         clsName: 'years',
15486         navFnc: 'FullYear',
15487         navStep: 10
15488     }]
15489 });
15490
15491 Roo.apply(Roo.bootstrap.DateField,  {
15492   
15493     template : {
15494         tag: 'div',
15495         cls: 'datepicker dropdown-menu roo-dynamic',
15496         cn: [
15497         {
15498             tag: 'div',
15499             cls: 'datepicker-days',
15500             cn: [
15501             {
15502                 tag: 'table',
15503                 cls: 'table-condensed',
15504                 cn:[
15505                 Roo.bootstrap.DateField.head,
15506                 {
15507                     tag: 'tbody'
15508                 },
15509                 Roo.bootstrap.DateField.footer
15510                 ]
15511             }
15512             ]
15513         },
15514         {
15515             tag: 'div',
15516             cls: 'datepicker-months',
15517             cn: [
15518             {
15519                 tag: 'table',
15520                 cls: 'table-condensed',
15521                 cn:[
15522                 Roo.bootstrap.DateField.head,
15523                 Roo.bootstrap.DateField.content,
15524                 Roo.bootstrap.DateField.footer
15525                 ]
15526             }
15527             ]
15528         },
15529         {
15530             tag: 'div',
15531             cls: 'datepicker-years',
15532             cn: [
15533             {
15534                 tag: 'table',
15535                 cls: 'table-condensed',
15536                 cn:[
15537                 Roo.bootstrap.DateField.head,
15538                 Roo.bootstrap.DateField.content,
15539                 Roo.bootstrap.DateField.footer
15540                 ]
15541             }
15542             ]
15543         }
15544         ]
15545     }
15546 });
15547
15548  
15549
15550  /*
15551  * - LGPL
15552  *
15553  * TimeField
15554  * 
15555  */
15556
15557 /**
15558  * @class Roo.bootstrap.TimeField
15559  * @extends Roo.bootstrap.Input
15560  * Bootstrap DateField class
15561  * 
15562  * 
15563  * @constructor
15564  * Create a new TimeField
15565  * @param {Object} config The config object
15566  */
15567
15568 Roo.bootstrap.TimeField = function(config){
15569     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
15570     this.addEvents({
15571             /**
15572              * @event show
15573              * Fires when this field show.
15574              * @param {Roo.bootstrap.DateField} this
15575              * @param {Mixed} date The date value
15576              */
15577             show : true,
15578             /**
15579              * @event show
15580              * Fires when this field hide.
15581              * @param {Roo.bootstrap.DateField} this
15582              * @param {Mixed} date The date value
15583              */
15584             hide : true,
15585             /**
15586              * @event select
15587              * Fires when select a date.
15588              * @param {Roo.bootstrap.DateField} this
15589              * @param {Mixed} date The date value
15590              */
15591             select : true
15592         });
15593 };
15594
15595 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
15596     
15597     /**
15598      * @cfg {String} format
15599      * The default time format string which can be overriden for localization support.  The format must be
15600      * valid according to {@link Date#parseDate} (defaults to 'H:i').
15601      */
15602     format : "H:i",
15603        
15604     onRender: function(ct, position)
15605     {
15606         
15607         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
15608                 
15609         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
15610         
15611         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15612         
15613         this.pop = this.picker().select('>.datepicker-time',true).first();
15614         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block' 
15615         
15616         this.picker().on('mousedown', this.onMousedown, this);
15617         this.picker().on('click', this.onClick, this);
15618         
15619         this.picker().addClass('datepicker-dropdown');
15620     
15621         this.fillTime();
15622         this.update();
15623             
15624         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
15625         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
15626         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
15627         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
15628         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
15629         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
15630
15631     },
15632     
15633     fireKey: function(e){
15634         if (!this.picker().isVisible()){
15635             if (e.keyCode == 27) // allow escape to hide and re-show picker
15636                 this.show();
15637             return;
15638         }
15639
15640         e.preventDefault();
15641         
15642         switch(e.keyCode){
15643             case 27: // escape
15644                 this.hide();
15645                 break;
15646             case 37: // left
15647             case 39: // right
15648                 this.onTogglePeriod();
15649                 break;
15650             case 38: // up
15651                 this.onIncrementMinutes();
15652                 break;
15653             case 40: // down
15654                 this.onDecrementMinutes();
15655                 break;
15656             case 13: // enter
15657             case 9: // tab
15658                 this.setTime();
15659                 break;
15660         }
15661     },
15662     
15663     onClick: function(e) {
15664         e.stopPropagation();
15665         e.preventDefault();
15666     },
15667     
15668     picker : function()
15669     {
15670         return this.el.select('.datepicker', true).first();
15671     },
15672     
15673     fillTime: function()
15674     {    
15675         var time = this.pop.select('tbody', true).first();
15676         
15677         time.dom.innerHTML = '';
15678         
15679         time.createChild({
15680             tag: 'tr',
15681             cn: [
15682                 {
15683                     tag: 'td',
15684                     cn: [
15685                         {
15686                             tag: 'a',
15687                             href: '#',
15688                             cls: 'btn',
15689                             cn: [
15690                                 {
15691                                     tag: 'span',
15692                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
15693                                 }
15694                             ]
15695                         } 
15696                     ]
15697                 },
15698                 {
15699                     tag: 'td',
15700                     cls: 'separator'
15701                 },
15702                 {
15703                     tag: 'td',
15704                     cn: [
15705                         {
15706                             tag: 'a',
15707                             href: '#',
15708                             cls: 'btn',
15709                             cn: [
15710                                 {
15711                                     tag: 'span',
15712                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
15713                                 }
15714                             ]
15715                         }
15716                     ]
15717                 },
15718                 {
15719                     tag: 'td',
15720                     cls: 'separator'
15721                 }
15722             ]
15723         });
15724         
15725         time.createChild({
15726             tag: 'tr',
15727             cn: [
15728                 {
15729                     tag: 'td',
15730                     cn: [
15731                         {
15732                             tag: 'span',
15733                             cls: 'timepicker-hour',
15734                             html: '00'
15735                         }  
15736                     ]
15737                 },
15738                 {
15739                     tag: 'td',
15740                     cls: 'separator',
15741                     html: ':'
15742                 },
15743                 {
15744                     tag: 'td',
15745                     cn: [
15746                         {
15747                             tag: 'span',
15748                             cls: 'timepicker-minute',
15749                             html: '00'
15750                         }  
15751                     ]
15752                 },
15753                 {
15754                     tag: 'td',
15755                     cls: 'separator'
15756                 },
15757                 {
15758                     tag: 'td',
15759                     cn: [
15760                         {
15761                             tag: 'button',
15762                             type: 'button',
15763                             cls: 'btn btn-primary period',
15764                             html: 'AM'
15765                             
15766                         }
15767                     ]
15768                 }
15769             ]
15770         });
15771         
15772         time.createChild({
15773             tag: 'tr',
15774             cn: [
15775                 {
15776                     tag: 'td',
15777                     cn: [
15778                         {
15779                             tag: 'a',
15780                             href: '#',
15781                             cls: 'btn',
15782                             cn: [
15783                                 {
15784                                     tag: 'span',
15785                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
15786                                 }
15787                             ]
15788                         }
15789                     ]
15790                 },
15791                 {
15792                     tag: 'td',
15793                     cls: 'separator'
15794                 },
15795                 {
15796                     tag: 'td',
15797                     cn: [
15798                         {
15799                             tag: 'a',
15800                             href: '#',
15801                             cls: 'btn',
15802                             cn: [
15803                                 {
15804                                     tag: 'span',
15805                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
15806                                 }
15807                             ]
15808                         }
15809                     ]
15810                 },
15811                 {
15812                     tag: 'td',
15813                     cls: 'separator'
15814                 }
15815             ]
15816         });
15817         
15818     },
15819     
15820     update: function()
15821     {
15822         
15823         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
15824         
15825         this.fill();
15826     },
15827     
15828     fill: function() 
15829     {
15830         var hours = this.time.getHours();
15831         var minutes = this.time.getMinutes();
15832         var period = 'AM';
15833         
15834         if(hours > 11){
15835             period = 'PM';
15836         }
15837         
15838         if(hours == 0){
15839             hours = 12;
15840         }
15841         
15842         
15843         if(hours > 12){
15844             hours = hours - 12;
15845         }
15846         
15847         if(hours < 10){
15848             hours = '0' + hours;
15849         }
15850         
15851         if(minutes < 10){
15852             minutes = '0' + minutes;
15853         }
15854         
15855         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
15856         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
15857         this.pop.select('button', true).first().dom.innerHTML = period;
15858         
15859     },
15860     
15861     place: function()
15862     {   
15863         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
15864         
15865         var cls = ['bottom'];
15866         
15867         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
15868             cls.pop();
15869             cls.push('top');
15870         }
15871         
15872         cls.push('right');
15873         
15874         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
15875             cls.pop();
15876             cls.push('left');
15877         }
15878         
15879         this.picker().addClass(cls.join('-'));
15880         
15881         var _this = this;
15882         
15883         Roo.each(cls, function(c){
15884             if(c == 'bottom'){
15885                 _this.picker().setTop(_this.inputEl().getHeight());
15886                 return;
15887             }
15888             if(c == 'top'){
15889                 _this.picker().setTop(0 - _this.picker().getHeight());
15890                 return;
15891             }
15892             
15893             if(c == 'left'){
15894                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
15895                 return;
15896             }
15897             if(c == 'right'){
15898                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
15899                 return;
15900             }
15901         });
15902         
15903     },
15904   
15905     onFocus : function()
15906     {
15907         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
15908         this.show();
15909     },
15910     
15911     onBlur : function()
15912     {
15913         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
15914         this.hide();
15915     },
15916     
15917     show : function()
15918     {
15919         this.picker().show();
15920         this.pop.show();
15921         this.update();
15922         this.place();
15923         
15924         this.fireEvent('show', this, this.date);
15925     },
15926     
15927     hide : function()
15928     {
15929         this.picker().hide();
15930         this.pop.hide();
15931         
15932         this.fireEvent('hide', this, this.date);
15933     },
15934     
15935     setTime : function()
15936     {
15937         this.hide();
15938         this.setValue(this.time.format(this.format));
15939         
15940         this.fireEvent('select', this, this.date);
15941         
15942         
15943     },
15944     
15945     onMousedown: function(e){
15946         e.stopPropagation();
15947         e.preventDefault();
15948     },
15949     
15950     onIncrementHours: function()
15951     {
15952         Roo.log('onIncrementHours');
15953         this.time = this.time.add(Date.HOUR, 1);
15954         this.update();
15955         
15956     },
15957     
15958     onDecrementHours: function()
15959     {
15960         Roo.log('onDecrementHours');
15961         this.time = this.time.add(Date.HOUR, -1);
15962         this.update();
15963     },
15964     
15965     onIncrementMinutes: function()
15966     {
15967         Roo.log('onIncrementMinutes');
15968         this.time = this.time.add(Date.MINUTE, 1);
15969         this.update();
15970     },
15971     
15972     onDecrementMinutes: function()
15973     {
15974         Roo.log('onDecrementMinutes');
15975         this.time = this.time.add(Date.MINUTE, -1);
15976         this.update();
15977     },
15978     
15979     onTogglePeriod: function()
15980     {
15981         Roo.log('onTogglePeriod');
15982         this.time = this.time.add(Date.HOUR, 12);
15983         this.update();
15984     }
15985     
15986    
15987 });
15988
15989 Roo.apply(Roo.bootstrap.TimeField,  {
15990     
15991     content : {
15992         tag: 'tbody',
15993         cn: [
15994             {
15995                 tag: 'tr',
15996                 cn: [
15997                 {
15998                     tag: 'td',
15999                     colspan: '7'
16000                 }
16001                 ]
16002             }
16003         ]
16004     },
16005     
16006     footer : {
16007         tag: 'tfoot',
16008         cn: [
16009             {
16010                 tag: 'tr',
16011                 cn: [
16012                 {
16013                     tag: 'th',
16014                     colspan: '7',
16015                     cls: '',
16016                     cn: [
16017                         {
16018                             tag: 'button',
16019                             cls: 'btn btn-info ok',
16020                             html: 'OK'
16021                         }
16022                     ]
16023                 }
16024
16025                 ]
16026             }
16027         ]
16028     }
16029 });
16030
16031 Roo.apply(Roo.bootstrap.TimeField,  {
16032   
16033     template : {
16034         tag: 'div',
16035         cls: 'datepicker dropdown-menu',
16036         cn: [
16037             {
16038                 tag: 'div',
16039                 cls: 'datepicker-time',
16040                 cn: [
16041                 {
16042                     tag: 'table',
16043                     cls: 'table-condensed',
16044                     cn:[
16045                     Roo.bootstrap.TimeField.content,
16046                     Roo.bootstrap.TimeField.footer
16047                     ]
16048                 }
16049                 ]
16050             }
16051         ]
16052     }
16053 });
16054
16055  
16056
16057  /*
16058  * - LGPL
16059  *
16060  * CheckBox
16061  * 
16062  */
16063
16064 /**
16065  * @class Roo.bootstrap.CheckBox
16066  * @extends Roo.bootstrap.Input
16067  * Bootstrap CheckBox class
16068  * 
16069  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
16070  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
16071  * @cfg {String} boxLabel The text that appears beside the checkbox
16072  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
16073  * @cfg {Boolean} checked initnal the element
16074  * @cfg {Boolean} inline inline the element (default false)
16075  * 
16076  * @constructor
16077  * Create a new CheckBox
16078  * @param {Object} config The config object
16079  */
16080
16081 Roo.bootstrap.CheckBox = function(config){
16082     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
16083    
16084         this.addEvents({
16085             /**
16086             * @event check
16087             * Fires when the element is checked or unchecked.
16088             * @param {Roo.bootstrap.CheckBox} this This input
16089             * @param {Boolean} checked The new checked value
16090             */
16091            check : true
16092         });
16093 };
16094
16095 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
16096     
16097     inputType: 'checkbox',
16098     inputValue: 1,
16099     valueOff: 0,
16100     boxLabel: false,
16101     checked: false,
16102     weight : false,
16103     inline: false,
16104     
16105     getAutoCreate : function()
16106     {
16107         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
16108         
16109         var id = Roo.id();
16110         
16111         var cfg = {};
16112         
16113         cfg.cls = 'form-group ' + this.inputType //input-group
16114         
16115         if(this.inline){
16116             cfg.cls += ' ' + this.inputType + '-inline';
16117         }
16118         
16119         var input =  {
16120             tag: 'input',
16121             id : id,
16122             type : this.inputType,
16123             value : (!this.checked) ? this.valueOff : this.inputValue,
16124             cls : 'roo-' + this.inputType, //'form-box',
16125             placeholder : this.placeholder || ''
16126             
16127         };
16128         
16129         if (this.weight) { // Validity check?
16130             cfg.cls += " " + this.inputType + "-" + this.weight;
16131         }
16132         
16133         if (this.disabled) {
16134             input.disabled=true;
16135         }
16136         
16137         if(this.checked){
16138             input.checked = this.checked;
16139         }
16140         
16141         if (this.name) {
16142             input.name = this.name;
16143         }
16144         
16145         if (this.size) {
16146             input.cls += ' input-' + this.size;
16147         }
16148         
16149         var settings=this;
16150         ['xs','sm','md','lg'].map(function(size){
16151             if (settings[size]) {
16152                 cfg.cls += ' col-' + size + '-' + settings[size];
16153             }
16154         });
16155         
16156        
16157         
16158         var inputblock = input;
16159         
16160         
16161         
16162         
16163         if (this.before || this.after) {
16164             
16165             inputblock = {
16166                 cls : 'input-group',
16167                 cn :  [] 
16168             };
16169             if (this.before) {
16170                 inputblock.cn.push({
16171                     tag :'span',
16172                     cls : 'input-group-addon',
16173                     html : this.before
16174                 });
16175             }
16176             inputblock.cn.push(input);
16177             if (this.after) {
16178                 inputblock.cn.push({
16179                     tag :'span',
16180                     cls : 'input-group-addon',
16181                     html : this.after
16182                 });
16183             }
16184             
16185         };
16186         
16187         if (align ==='left' && this.fieldLabel.length) {
16188                 Roo.log("left and has label");
16189                 cfg.cn = [
16190                     
16191                     {
16192                         tag: 'label',
16193                         'for' :  id,
16194                         cls : 'control-label col-md-' + this.labelWidth,
16195                         html : this.fieldLabel
16196                         
16197                     },
16198                     {
16199                         cls : "col-md-" + (12 - this.labelWidth), 
16200                         cn: [
16201                             inputblock
16202                         ]
16203                     }
16204                     
16205                 ];
16206         } else if ( this.fieldLabel.length) {
16207                 Roo.log(" label");
16208                 cfg.cn = [
16209                    
16210                     {
16211                         tag: this.boxLabel ? 'span' : 'label',
16212                         'for': id,
16213                         cls: 'control-label box-input-label',
16214                         //cls : 'input-group-addon',
16215                         html : this.fieldLabel
16216                         
16217                     },
16218                     
16219                     inputblock
16220                     
16221                 ];
16222
16223         } else {
16224             
16225                 Roo.log(" no label && no align");
16226                 cfg.cn = [  inputblock ] ;
16227                 
16228                 
16229         };
16230          if(this.boxLabel){
16231             cfg.cn.push( {
16232                 tag: 'label',
16233                 //'for': id, // box label is handled by onclick - so no for...
16234                 cls: 'box-label',
16235                 html: this.boxLabel
16236                 
16237             });
16238         }
16239         
16240         
16241        
16242         return cfg;
16243         
16244     },
16245     
16246     /**
16247      * return the real input element.
16248      */
16249     inputEl: function ()
16250     {
16251         return this.el.select('input.roo-' + this.inputType,true).first();
16252     },
16253     
16254     labelEl: function()
16255     {
16256         return this.el.select('label.control-label',true).first();
16257     },
16258     /* depricated... */
16259     
16260     label: function()
16261     {
16262         return this.labelEl();
16263     },
16264     
16265     initEvents : function()
16266     {
16267 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
16268         
16269         this.inputEl().on('click', this.onClick,  this);
16270         if (this.boxLabel) { 
16271             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
16272         }
16273         
16274     },
16275     
16276     onClick : function()
16277     {   
16278         this.setChecked(!this.checked);
16279     },
16280     
16281     setChecked : function(state,suppressEvent)
16282     {
16283         if(this.inputType == 'radio'){
16284             
16285             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
16286                 e.dom.checked = false;
16287             });
16288             
16289             this.inputEl().dom.checked = true;
16290             
16291             if(suppressEvent !== true){
16292                 this.fireEvent('check', this, true);
16293             }
16294             
16295             this.inputEl().dom.value = this.inputValue;
16296             
16297             return;
16298         }
16299         
16300         this.checked = state;
16301         
16302         if(suppressEvent !== true){
16303             this.fireEvent('check', this, state);
16304         }
16305         
16306         this.inputEl().dom.checked = state;
16307         
16308         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
16309         
16310     },
16311     
16312     getValue : function()
16313     {
16314         if(this.inputType == 'radio'){
16315             return this.getGroupValue();
16316         }
16317         
16318         return this.inputEl().getValue();
16319         
16320     },
16321     
16322     getGroupValue : function()
16323     {
16324         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
16325     },
16326     
16327     setValue : function(v,suppressEvent)
16328     {
16329         if(this.inputType == 'radio'){
16330             this.setGroupValue(v, suppressEvent);
16331             return;
16332         }
16333         
16334         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
16335     },
16336     
16337     setGroupValue : function(v, suppressEvent)
16338     {
16339         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
16340             e.dom.checked = false;
16341             
16342             if(e.dom.value == v){
16343                 e.dom.checked = true;
16344             }
16345         });
16346
16347         if(suppressEvent !== true){
16348             this.fireEvent('check', this, true);
16349         }
16350
16351         return;
16352     }
16353     
16354 });
16355
16356  
16357 /*
16358  * - LGPL
16359  *
16360  * Radio
16361  * 
16362  */
16363
16364 /**
16365  * @class Roo.bootstrap.Radio
16366  * @extends Roo.bootstrap.CheckBox
16367  * Bootstrap Radio class
16368
16369  * @constructor
16370  * Create a new Radio
16371  * @param {Object} config The config object
16372  */
16373
16374 Roo.bootstrap.Radio = function(config){
16375     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
16376    
16377 };
16378
16379 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
16380     
16381     inputType: 'radio',
16382     inputValue: '',
16383     valueOff: '',
16384     
16385     getAutoCreate : function()
16386     {
16387         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
16388         
16389         var id = Roo.id();
16390         
16391         var cfg = {};
16392         
16393         cfg.cls = 'form-group radio' //input-group
16394         
16395         var input =  {
16396             tag: 'input',
16397             id : id,
16398             type : this.inputType,
16399             //value : (!this.checked) ? this.valueOff : this.inputValue,
16400             value : this.inputValue,
16401             cls : 'roo-radio',
16402             placeholder : this.placeholder || ''
16403             
16404         };
16405           if (this.weight) { // Validity check?
16406             cfg.cls += " radio-" + this.weight;
16407         }
16408         if (this.disabled) {
16409             input.disabled=true;
16410         }
16411         
16412         if(this.checked){
16413             input.checked = this.checked;
16414         }
16415         
16416         if (this.name) {
16417             input.name = this.name;
16418         }
16419         
16420         if (this.size) {
16421             input.cls += ' input-' + this.size;
16422         }
16423         
16424         var settings=this;
16425         ['xs','sm','md','lg'].map(function(size){
16426             if (settings[size]) {
16427                 cfg.cls += ' col-' + size + '-' + settings[size];
16428             }
16429         });
16430         
16431         var inputblock = input;
16432         
16433         if (this.before || this.after) {
16434             
16435             inputblock = {
16436                 cls : 'input-group',
16437                 cn :  [] 
16438             };
16439             if (this.before) {
16440                 inputblock.cn.push({
16441                     tag :'span',
16442                     cls : 'input-group-addon',
16443                     html : this.before
16444                 });
16445             }
16446             inputblock.cn.push(input);
16447             if (this.after) {
16448                 inputblock.cn.push({
16449                     tag :'span',
16450                     cls : 'input-group-addon',
16451                     html : this.after
16452                 });
16453             }
16454             
16455         };
16456         
16457         if (align ==='left' && this.fieldLabel.length) {
16458                 Roo.log("left and has label");
16459                 cfg.cn = [
16460                     
16461                     {
16462                         tag: 'label',
16463                         'for' :  id,
16464                         cls : 'control-label col-md-' + this.labelWidth,
16465                         html : this.fieldLabel
16466                         
16467                     },
16468                     {
16469                         cls : "col-md-" + (12 - this.labelWidth), 
16470                         cn: [
16471                             inputblock
16472                         ]
16473                     }
16474                     
16475                 ];
16476         } else if ( this.fieldLabel.length) {
16477                 Roo.log(" label");
16478                  cfg.cn = [
16479                    
16480                     {
16481                         tag: 'label',
16482                         'for': id,
16483                         cls: 'control-label box-input-label',
16484                         //cls : 'input-group-addon',
16485                         html : this.fieldLabel
16486                         
16487                     },
16488                     
16489                     inputblock
16490                     
16491                 ];
16492
16493         } else {
16494             
16495                    Roo.log(" no label && no align");
16496                 cfg.cn = [
16497                     
16498                         inputblock
16499                     
16500                 ];
16501                 
16502                 
16503         };
16504         
16505         if(this.boxLabel){
16506             cfg.cn.push({
16507                 tag: 'label',
16508                 'for': id,
16509                 cls: 'box-label',
16510                 html: this.boxLabel
16511             })
16512         }
16513         
16514         return cfg;
16515         
16516     },
16517     inputEl: function ()
16518     {
16519         return this.el.select('input.roo-radio',true).first();
16520     },
16521     onClick : function()
16522     {   
16523         Roo.log("click");
16524         Roo.log("click");
16525         this.setChecked(true);
16526     },
16527     
16528     setChecked : function(state,suppressEvent)
16529     {
16530         if(state){
16531             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16532                 v.dom.checked = false;
16533             });
16534         }
16535         
16536         this.checked = state;
16537         this.inputEl().dom.checked = state;
16538         
16539         if(suppressEvent !== true){
16540             this.fireEvent('check', this, state);
16541         }
16542         
16543         //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
16544         
16545     },
16546     
16547     getGroupValue : function()
16548     {
16549         var value = ''
16550         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16551             if(v.dom.checked == true){
16552                 value = v.dom.value;
16553             }
16554         });
16555         
16556         return value;
16557     },
16558     
16559     /**
16560      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
16561      * @return {Mixed} value The field value
16562      */
16563     getValue : function(){
16564         return this.getGroupValue();
16565     }
16566     
16567 });
16568
16569  
16570 //<script type="text/javascript">
16571
16572 /*
16573  * Based  Ext JS Library 1.1.1
16574  * Copyright(c) 2006-2007, Ext JS, LLC.
16575  * LGPL
16576  *
16577  */
16578  
16579 /**
16580  * @class Roo.HtmlEditorCore
16581  * @extends Roo.Component
16582  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
16583  *
16584  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
16585  */
16586
16587 Roo.HtmlEditorCore = function(config){
16588     
16589     
16590     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
16591     
16592     
16593     this.addEvents({
16594         /**
16595          * @event initialize
16596          * Fires when the editor is fully initialized (including the iframe)
16597          * @param {Roo.HtmlEditorCore} this
16598          */
16599         initialize: true,
16600         /**
16601          * @event activate
16602          * Fires when the editor is first receives the focus. Any insertion must wait
16603          * until after this event.
16604          * @param {Roo.HtmlEditorCore} this
16605          */
16606         activate: true,
16607          /**
16608          * @event beforesync
16609          * Fires before the textarea is updated with content from the editor iframe. Return false
16610          * to cancel the sync.
16611          * @param {Roo.HtmlEditorCore} this
16612          * @param {String} html
16613          */
16614         beforesync: true,
16615          /**
16616          * @event beforepush
16617          * Fires before the iframe editor is updated with content from the textarea. Return false
16618          * to cancel the push.
16619          * @param {Roo.HtmlEditorCore} this
16620          * @param {String} html
16621          */
16622         beforepush: true,
16623          /**
16624          * @event sync
16625          * Fires when the textarea is updated with content from the editor iframe.
16626          * @param {Roo.HtmlEditorCore} this
16627          * @param {String} html
16628          */
16629         sync: true,
16630          /**
16631          * @event push
16632          * Fires when the iframe editor is updated with content from the textarea.
16633          * @param {Roo.HtmlEditorCore} this
16634          * @param {String} html
16635          */
16636         push: true,
16637         
16638         /**
16639          * @event editorevent
16640          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
16641          * @param {Roo.HtmlEditorCore} this
16642          */
16643         editorevent: true
16644     });
16645     
16646     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
16647     
16648     // defaults : white / black...
16649     this.applyBlacklists();
16650     
16651     
16652     
16653 };
16654
16655
16656 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
16657
16658
16659      /**
16660      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
16661      */
16662     
16663     owner : false,
16664     
16665      /**
16666      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
16667      *                        Roo.resizable.
16668      */
16669     resizable : false,
16670      /**
16671      * @cfg {Number} height (in pixels)
16672      */   
16673     height: 300,
16674    /**
16675      * @cfg {Number} width (in pixels)
16676      */   
16677     width: 500,
16678     
16679     /**
16680      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
16681      * 
16682      */
16683     stylesheets: false,
16684     
16685     // id of frame..
16686     frameId: false,
16687     
16688     // private properties
16689     validationEvent : false,
16690     deferHeight: true,
16691     initialized : false,
16692     activated : false,
16693     sourceEditMode : false,
16694     onFocus : Roo.emptyFn,
16695     iframePad:3,
16696     hideMode:'offsets',
16697     
16698     clearUp: true,
16699     
16700     // blacklist + whitelisted elements..
16701     black: false,
16702     white: false,
16703      
16704     
16705
16706     /**
16707      * Protected method that will not generally be called directly. It
16708      * is called when the editor initializes the iframe with HTML contents. Override this method if you
16709      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
16710      */
16711     getDocMarkup : function(){
16712         // body styles..
16713         var st = '';
16714         Roo.log(this.stylesheets);
16715         
16716         // inherit styels from page...?? 
16717         if (this.stylesheets === false) {
16718             
16719             Roo.get(document.head).select('style').each(function(node) {
16720                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16721             });
16722             
16723             Roo.get(document.head).select('link').each(function(node) { 
16724                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16725             });
16726             
16727         } else if (!this.stylesheets.length) {
16728                 // simple..
16729                 st = '<style type="text/css">' +
16730                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16731                    '</style>';
16732         } else {
16733             Roo.each(this.stylesheets, function(s) {
16734                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
16735             });
16736             
16737         }
16738         
16739         st +=  '<style type="text/css">' +
16740             'IMG { cursor: pointer } ' +
16741         '</style>';
16742
16743         
16744         return '<html><head>' + st  +
16745             //<style type="text/css">' +
16746             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16747             //'</style>' +
16748             ' </head><body class="roo-htmleditor-body"></body></html>';
16749     },
16750
16751     // private
16752     onRender : function(ct, position)
16753     {
16754         var _t = this;
16755         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
16756         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
16757         
16758         
16759         this.el.dom.style.border = '0 none';
16760         this.el.dom.setAttribute('tabIndex', -1);
16761         this.el.addClass('x-hidden hide');
16762         
16763         
16764         
16765         if(Roo.isIE){ // fix IE 1px bogus margin
16766             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
16767         }
16768        
16769         
16770         this.frameId = Roo.id();
16771         
16772          
16773         
16774         var iframe = this.owner.wrap.createChild({
16775             tag: 'iframe',
16776             cls: 'form-control', // bootstrap..
16777             id: this.frameId,
16778             name: this.frameId,
16779             frameBorder : 'no',
16780             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
16781         }, this.el
16782         );
16783         
16784         
16785         this.iframe = iframe.dom;
16786
16787          this.assignDocWin();
16788         
16789         this.doc.designMode = 'on';
16790        
16791         this.doc.open();
16792         this.doc.write(this.getDocMarkup());
16793         this.doc.close();
16794
16795         
16796         var task = { // must defer to wait for browser to be ready
16797             run : function(){
16798                 //console.log("run task?" + this.doc.readyState);
16799                 this.assignDocWin();
16800                 if(this.doc.body || this.doc.readyState == 'complete'){
16801                     try {
16802                         this.doc.designMode="on";
16803                     } catch (e) {
16804                         return;
16805                     }
16806                     Roo.TaskMgr.stop(task);
16807                     this.initEditor.defer(10, this);
16808                 }
16809             },
16810             interval : 10,
16811             duration: 10000,
16812             scope: this
16813         };
16814         Roo.TaskMgr.start(task);
16815
16816         
16817          
16818     },
16819
16820     // private
16821     onResize : function(w, h)
16822     {
16823          Roo.log('resize: ' +w + ',' + h );
16824         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
16825         if(!this.iframe){
16826             return;
16827         }
16828         if(typeof w == 'number'){
16829             
16830             this.iframe.style.width = w + 'px';
16831         }
16832         if(typeof h == 'number'){
16833             
16834             this.iframe.style.height = h + 'px';
16835             if(this.doc){
16836                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
16837             }
16838         }
16839         
16840     },
16841
16842     /**
16843      * Toggles the editor between standard and source edit mode.
16844      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
16845      */
16846     toggleSourceEdit : function(sourceEditMode){
16847         
16848         this.sourceEditMode = sourceEditMode === true;
16849         
16850         if(this.sourceEditMode){
16851  
16852             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
16853             
16854         }else{
16855             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
16856             //this.iframe.className = '';
16857             this.deferFocus();
16858         }
16859         //this.setSize(this.owner.wrap.getSize());
16860         //this.fireEvent('editmodechange', this, this.sourceEditMode);
16861     },
16862
16863     
16864   
16865
16866     /**
16867      * Protected method that will not generally be called directly. If you need/want
16868      * custom HTML cleanup, this is the method you should override.
16869      * @param {String} html The HTML to be cleaned
16870      * return {String} The cleaned HTML
16871      */
16872     cleanHtml : function(html){
16873         html = String(html);
16874         if(html.length > 5){
16875             if(Roo.isSafari){ // strip safari nonsense
16876                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
16877             }
16878         }
16879         if(html == '&nbsp;'){
16880             html = '';
16881         }
16882         return html;
16883     },
16884
16885     /**
16886      * HTML Editor -> Textarea
16887      * Protected method that will not generally be called directly. Syncs the contents
16888      * of the editor iframe with the textarea.
16889      */
16890     syncValue : function(){
16891         if(this.initialized){
16892             var bd = (this.doc.body || this.doc.documentElement);
16893             //this.cleanUpPaste(); -- this is done else where and causes havoc..
16894             var html = bd.innerHTML;
16895             if(Roo.isSafari){
16896                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
16897                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
16898                 if(m && m[1]){
16899                     html = '<div style="'+m[0]+'">' + html + '</div>';
16900                 }
16901             }
16902             html = this.cleanHtml(html);
16903             // fix up the special chars.. normaly like back quotes in word...
16904             // however we do not want to do this with chinese..
16905             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
16906                 var cc = b.charCodeAt();
16907                 if (
16908                     (cc >= 0x4E00 && cc < 0xA000 ) ||
16909                     (cc >= 0x3400 && cc < 0x4E00 ) ||
16910                     (cc >= 0xf900 && cc < 0xfb00 )
16911                 ) {
16912                         return b;
16913                 }
16914                 return "&#"+cc+";" 
16915             });
16916             if(this.owner.fireEvent('beforesync', this, html) !== false){
16917                 this.el.dom.value = html;
16918                 this.owner.fireEvent('sync', this, html);
16919             }
16920         }
16921     },
16922
16923     /**
16924      * Protected method that will not generally be called directly. Pushes the value of the textarea
16925      * into the iframe editor.
16926      */
16927     pushValue : function(){
16928         if(this.initialized){
16929             var v = this.el.dom.value.trim();
16930             
16931 //            if(v.length < 1){
16932 //                v = '&#160;';
16933 //            }
16934             
16935             if(this.owner.fireEvent('beforepush', this, v) !== false){
16936                 var d = (this.doc.body || this.doc.documentElement);
16937                 d.innerHTML = v;
16938                 this.cleanUpPaste();
16939                 this.el.dom.value = d.innerHTML;
16940                 this.owner.fireEvent('push', this, v);
16941             }
16942         }
16943     },
16944
16945     // private
16946     deferFocus : function(){
16947         this.focus.defer(10, this);
16948     },
16949
16950     // doc'ed in Field
16951     focus : function(){
16952         if(this.win && !this.sourceEditMode){
16953             this.win.focus();
16954         }else{
16955             this.el.focus();
16956         }
16957     },
16958     
16959     assignDocWin: function()
16960     {
16961         var iframe = this.iframe;
16962         
16963          if(Roo.isIE){
16964             this.doc = iframe.contentWindow.document;
16965             this.win = iframe.contentWindow;
16966         } else {
16967 //            if (!Roo.get(this.frameId)) {
16968 //                return;
16969 //            }
16970 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
16971 //            this.win = Roo.get(this.frameId).dom.contentWindow;
16972             
16973             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
16974                 return;
16975             }
16976             
16977             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
16978             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
16979         }
16980     },
16981     
16982     // private
16983     initEditor : function(){
16984         //console.log("INIT EDITOR");
16985         this.assignDocWin();
16986         
16987         
16988         
16989         this.doc.designMode="on";
16990         this.doc.open();
16991         this.doc.write(this.getDocMarkup());
16992         this.doc.close();
16993         
16994         var dbody = (this.doc.body || this.doc.documentElement);
16995         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
16996         // this copies styles from the containing element into thsi one..
16997         // not sure why we need all of this..
16998         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
16999         
17000         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
17001         //ss['background-attachment'] = 'fixed'; // w3c
17002         dbody.bgProperties = 'fixed'; // ie
17003         //Roo.DomHelper.applyStyles(dbody, ss);
17004         Roo.EventManager.on(this.doc, {
17005             //'mousedown': this.onEditorEvent,
17006             'mouseup': this.onEditorEvent,
17007             'dblclick': this.onEditorEvent,
17008             'click': this.onEditorEvent,
17009             'keyup': this.onEditorEvent,
17010             buffer:100,
17011             scope: this
17012         });
17013         if(Roo.isGecko){
17014             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
17015         }
17016         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
17017             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
17018         }
17019         this.initialized = true;
17020
17021         this.owner.fireEvent('initialize', this);
17022         this.pushValue();
17023     },
17024
17025     // private
17026     onDestroy : function(){
17027         
17028         
17029         
17030         if(this.rendered){
17031             
17032             //for (var i =0; i < this.toolbars.length;i++) {
17033             //    // fixme - ask toolbars for heights?
17034             //    this.toolbars[i].onDestroy();
17035            // }
17036             
17037             //this.wrap.dom.innerHTML = '';
17038             //this.wrap.remove();
17039         }
17040     },
17041
17042     // private
17043     onFirstFocus : function(){
17044         
17045         this.assignDocWin();
17046         
17047         
17048         this.activated = true;
17049          
17050     
17051         if(Roo.isGecko){ // prevent silly gecko errors
17052             this.win.focus();
17053             var s = this.win.getSelection();
17054             if(!s.focusNode || s.focusNode.nodeType != 3){
17055                 var r = s.getRangeAt(0);
17056                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
17057                 r.collapse(true);
17058                 this.deferFocus();
17059             }
17060             try{
17061                 this.execCmd('useCSS', true);
17062                 this.execCmd('styleWithCSS', false);
17063             }catch(e){}
17064         }
17065         this.owner.fireEvent('activate', this);
17066     },
17067
17068     // private
17069     adjustFont: function(btn){
17070         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
17071         //if(Roo.isSafari){ // safari
17072         //    adjust *= 2;
17073        // }
17074         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
17075         if(Roo.isSafari){ // safari
17076             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
17077             v =  (v < 10) ? 10 : v;
17078             v =  (v > 48) ? 48 : v;
17079             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
17080             
17081         }
17082         
17083         
17084         v = Math.max(1, v+adjust);
17085         
17086         this.execCmd('FontSize', v  );
17087     },
17088
17089     onEditorEvent : function(e){
17090         this.owner.fireEvent('editorevent', this, e);
17091       //  this.updateToolbar();
17092         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
17093     },
17094
17095     insertTag : function(tg)
17096     {
17097         // could be a bit smarter... -> wrap the current selected tRoo..
17098         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
17099             
17100             range = this.createRange(this.getSelection());
17101             var wrappingNode = this.doc.createElement(tg.toLowerCase());
17102             wrappingNode.appendChild(range.extractContents());
17103             range.insertNode(wrappingNode);
17104
17105             return;
17106             
17107             
17108             
17109         }
17110         this.execCmd("formatblock",   tg);
17111         
17112     },
17113     
17114     insertText : function(txt)
17115     {
17116         
17117         
17118         var range = this.createRange();
17119         range.deleteContents();
17120                //alert(Sender.getAttribute('label'));
17121                
17122         range.insertNode(this.doc.createTextNode(txt));
17123     } ,
17124     
17125      
17126
17127     /**
17128      * Executes a Midas editor command on the editor document and performs necessary focus and
17129      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
17130      * @param {String} cmd The Midas command
17131      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
17132      */
17133     relayCmd : function(cmd, value){
17134         this.win.focus();
17135         this.execCmd(cmd, value);
17136         this.owner.fireEvent('editorevent', this);
17137         //this.updateToolbar();
17138         this.owner.deferFocus();
17139     },
17140
17141     /**
17142      * Executes a Midas editor command directly on the editor document.
17143      * For visual commands, you should use {@link #relayCmd} instead.
17144      * <b>This should only be called after the editor is initialized.</b>
17145      * @param {String} cmd The Midas command
17146      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
17147      */
17148     execCmd : function(cmd, value){
17149         this.doc.execCommand(cmd, false, value === undefined ? null : value);
17150         this.syncValue();
17151     },
17152  
17153  
17154    
17155     /**
17156      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
17157      * to insert tRoo.
17158      * @param {String} text | dom node.. 
17159      */
17160     insertAtCursor : function(text)
17161     {
17162         
17163         
17164         
17165         if(!this.activated){
17166             return;
17167         }
17168         /*
17169         if(Roo.isIE){
17170             this.win.focus();
17171             var r = this.doc.selection.createRange();
17172             if(r){
17173                 r.collapse(true);
17174                 r.pasteHTML(text);
17175                 this.syncValue();
17176                 this.deferFocus();
17177             
17178             }
17179             return;
17180         }
17181         */
17182         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
17183             this.win.focus();
17184             
17185             
17186             // from jquery ui (MIT licenced)
17187             var range, node;
17188             var win = this.win;
17189             
17190             if (win.getSelection && win.getSelection().getRangeAt) {
17191                 range = win.getSelection().getRangeAt(0);
17192                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
17193                 range.insertNode(node);
17194             } else if (win.document.selection && win.document.selection.createRange) {
17195                 // no firefox support
17196                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
17197                 win.document.selection.createRange().pasteHTML(txt);
17198             } else {
17199                 // no firefox support
17200                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
17201                 this.execCmd('InsertHTML', txt);
17202             } 
17203             
17204             this.syncValue();
17205             
17206             this.deferFocus();
17207         }
17208     },
17209  // private
17210     mozKeyPress : function(e){
17211         if(e.ctrlKey){
17212             var c = e.getCharCode(), cmd;
17213           
17214             if(c > 0){
17215                 c = String.fromCharCode(c).toLowerCase();
17216                 switch(c){
17217                     case 'b':
17218                         cmd = 'bold';
17219                         break;
17220                     case 'i':
17221                         cmd = 'italic';
17222                         break;
17223                     
17224                     case 'u':
17225                         cmd = 'underline';
17226                         break;
17227                     
17228                     case 'v':
17229                         this.cleanUpPaste.defer(100, this);
17230                         return;
17231                         
17232                 }
17233                 if(cmd){
17234                     this.win.focus();
17235                     this.execCmd(cmd);
17236                     this.deferFocus();
17237                     e.preventDefault();
17238                 }
17239                 
17240             }
17241         }
17242     },
17243
17244     // private
17245     fixKeys : function(){ // load time branching for fastest keydown performance
17246         if(Roo.isIE){
17247             return function(e){
17248                 var k = e.getKey(), r;
17249                 if(k == e.TAB){
17250                     e.stopEvent();
17251                     r = this.doc.selection.createRange();
17252                     if(r){
17253                         r.collapse(true);
17254                         r.pasteHTML('&#160;&#160;&#160;&#160;');
17255                         this.deferFocus();
17256                     }
17257                     return;
17258                 }
17259                 
17260                 if(k == e.ENTER){
17261                     r = this.doc.selection.createRange();
17262                     if(r){
17263                         var target = r.parentElement();
17264                         if(!target || target.tagName.toLowerCase() != 'li'){
17265                             e.stopEvent();
17266                             r.pasteHTML('<br />');
17267                             r.collapse(false);
17268                             r.select();
17269                         }
17270                     }
17271                 }
17272                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
17273                     this.cleanUpPaste.defer(100, this);
17274                     return;
17275                 }
17276                 
17277                 
17278             };
17279         }else if(Roo.isOpera){
17280             return function(e){
17281                 var k = e.getKey();
17282                 if(k == e.TAB){
17283                     e.stopEvent();
17284                     this.win.focus();
17285                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
17286                     this.deferFocus();
17287                 }
17288                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
17289                     this.cleanUpPaste.defer(100, this);
17290                     return;
17291                 }
17292                 
17293             };
17294         }else if(Roo.isSafari){
17295             return function(e){
17296                 var k = e.getKey();
17297                 
17298                 if(k == e.TAB){
17299                     e.stopEvent();
17300                     this.execCmd('InsertText','\t');
17301                     this.deferFocus();
17302                     return;
17303                 }
17304                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
17305                     this.cleanUpPaste.defer(100, this);
17306                     return;
17307                 }
17308                 
17309              };
17310         }
17311     }(),
17312     
17313     getAllAncestors: function()
17314     {
17315         var p = this.getSelectedNode();
17316         var a = [];
17317         if (!p) {
17318             a.push(p); // push blank onto stack..
17319             p = this.getParentElement();
17320         }
17321         
17322         
17323         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
17324             a.push(p);
17325             p = p.parentNode;
17326         }
17327         a.push(this.doc.body);
17328         return a;
17329     },
17330     lastSel : false,
17331     lastSelNode : false,
17332     
17333     
17334     getSelection : function() 
17335     {
17336         this.assignDocWin();
17337         return Roo.isIE ? this.doc.selection : this.win.getSelection();
17338     },
17339     
17340     getSelectedNode: function() 
17341     {
17342         // this may only work on Gecko!!!
17343         
17344         // should we cache this!!!!
17345         
17346         
17347         
17348          
17349         var range = this.createRange(this.getSelection()).cloneRange();
17350         
17351         if (Roo.isIE) {
17352             var parent = range.parentElement();
17353             while (true) {
17354                 var testRange = range.duplicate();
17355                 testRange.moveToElementText(parent);
17356                 if (testRange.inRange(range)) {
17357                     break;
17358                 }
17359                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
17360                     break;
17361                 }
17362                 parent = parent.parentElement;
17363             }
17364             return parent;
17365         }
17366         
17367         // is ancestor a text element.
17368         var ac =  range.commonAncestorContainer;
17369         if (ac.nodeType == 3) {
17370             ac = ac.parentNode;
17371         }
17372         
17373         var ar = ac.childNodes;
17374          
17375         var nodes = [];
17376         var other_nodes = [];
17377         var has_other_nodes = false;
17378         for (var i=0;i<ar.length;i++) {
17379             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
17380                 continue;
17381             }
17382             // fullly contained node.
17383             
17384             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
17385                 nodes.push(ar[i]);
17386                 continue;
17387             }
17388             
17389             // probably selected..
17390             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
17391                 other_nodes.push(ar[i]);
17392                 continue;
17393             }
17394             // outer..
17395             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
17396                 continue;
17397             }
17398             
17399             
17400             has_other_nodes = true;
17401         }
17402         if (!nodes.length && other_nodes.length) {
17403             nodes= other_nodes;
17404         }
17405         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
17406             return false;
17407         }
17408         
17409         return nodes[0];
17410     },
17411     createRange: function(sel)
17412     {
17413         // this has strange effects when using with 
17414         // top toolbar - not sure if it's a great idea.
17415         //this.editor.contentWindow.focus();
17416         if (typeof sel != "undefined") {
17417             try {
17418                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
17419             } catch(e) {
17420                 return this.doc.createRange();
17421             }
17422         } else {
17423             return this.doc.createRange();
17424         }
17425     },
17426     getParentElement: function()
17427     {
17428         
17429         this.assignDocWin();
17430         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
17431         
17432         var range = this.createRange(sel);
17433          
17434         try {
17435             var p = range.commonAncestorContainer;
17436             while (p.nodeType == 3) { // text node
17437                 p = p.parentNode;
17438             }
17439             return p;
17440         } catch (e) {
17441             return null;
17442         }
17443     
17444     },
17445     /***
17446      *
17447      * Range intersection.. the hard stuff...
17448      *  '-1' = before
17449      *  '0' = hits..
17450      *  '1' = after.
17451      *         [ -- selected range --- ]
17452      *   [fail]                        [fail]
17453      *
17454      *    basically..
17455      *      if end is before start or  hits it. fail.
17456      *      if start is after end or hits it fail.
17457      *
17458      *   if either hits (but other is outside. - then it's not 
17459      *   
17460      *    
17461      **/
17462     
17463     
17464     // @see http://www.thismuchiknow.co.uk/?p=64.
17465     rangeIntersectsNode : function(range, node)
17466     {
17467         var nodeRange = node.ownerDocument.createRange();
17468         try {
17469             nodeRange.selectNode(node);
17470         } catch (e) {
17471             nodeRange.selectNodeContents(node);
17472         }
17473     
17474         var rangeStartRange = range.cloneRange();
17475         rangeStartRange.collapse(true);
17476     
17477         var rangeEndRange = range.cloneRange();
17478         rangeEndRange.collapse(false);
17479     
17480         var nodeStartRange = nodeRange.cloneRange();
17481         nodeStartRange.collapse(true);
17482     
17483         var nodeEndRange = nodeRange.cloneRange();
17484         nodeEndRange.collapse(false);
17485     
17486         return rangeStartRange.compareBoundaryPoints(
17487                  Range.START_TO_START, nodeEndRange) == -1 &&
17488                rangeEndRange.compareBoundaryPoints(
17489                  Range.START_TO_START, nodeStartRange) == 1;
17490         
17491          
17492     },
17493     rangeCompareNode : function(range, node)
17494     {
17495         var nodeRange = node.ownerDocument.createRange();
17496         try {
17497             nodeRange.selectNode(node);
17498         } catch (e) {
17499             nodeRange.selectNodeContents(node);
17500         }
17501         
17502         
17503         range.collapse(true);
17504     
17505         nodeRange.collapse(true);
17506      
17507         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
17508         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
17509          
17510         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
17511         
17512         var nodeIsBefore   =  ss == 1;
17513         var nodeIsAfter    = ee == -1;
17514         
17515         if (nodeIsBefore && nodeIsAfter)
17516             return 0; // outer
17517         if (!nodeIsBefore && nodeIsAfter)
17518             return 1; //right trailed.
17519         
17520         if (nodeIsBefore && !nodeIsAfter)
17521             return 2;  // left trailed.
17522         // fully contined.
17523         return 3;
17524     },
17525
17526     // private? - in a new class?
17527     cleanUpPaste :  function()
17528     {
17529         // cleans up the whole document..
17530         Roo.log('cleanuppaste');
17531         
17532         this.cleanUpChildren(this.doc.body);
17533         var clean = this.cleanWordChars(this.doc.body.innerHTML);
17534         if (clean != this.doc.body.innerHTML) {
17535             this.doc.body.innerHTML = clean;
17536         }
17537         
17538     },
17539     
17540     cleanWordChars : function(input) {// change the chars to hex code
17541         var he = Roo.HtmlEditorCore;
17542         
17543         var output = input;
17544         Roo.each(he.swapCodes, function(sw) { 
17545             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
17546             
17547             output = output.replace(swapper, sw[1]);
17548         });
17549         
17550         return output;
17551     },
17552     
17553     
17554     cleanUpChildren : function (n)
17555     {
17556         if (!n.childNodes.length) {
17557             return;
17558         }
17559         for (var i = n.childNodes.length-1; i > -1 ; i--) {
17560            this.cleanUpChild(n.childNodes[i]);
17561         }
17562     },
17563     
17564     
17565         
17566     
17567     cleanUpChild : function (node)
17568     {
17569         var ed = this;
17570         //console.log(node);
17571         if (node.nodeName == "#text") {
17572             // clean up silly Windows -- stuff?
17573             return; 
17574         }
17575         if (node.nodeName == "#comment") {
17576             node.parentNode.removeChild(node);
17577             // clean up silly Windows -- stuff?
17578             return; 
17579         }
17580         var lcname = node.tagName.toLowerCase();
17581         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
17582         // whitelist of tags..
17583         
17584         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
17585             // remove node.
17586             node.parentNode.removeChild(node);
17587             return;
17588             
17589         }
17590         
17591         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
17592         
17593         // remove <a name=....> as rendering on yahoo mailer is borked with this.
17594         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
17595         
17596         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
17597         //    remove_keep_children = true;
17598         //}
17599         
17600         if (remove_keep_children) {
17601             this.cleanUpChildren(node);
17602             // inserts everything just before this node...
17603             while (node.childNodes.length) {
17604                 var cn = node.childNodes[0];
17605                 node.removeChild(cn);
17606                 node.parentNode.insertBefore(cn, node);
17607             }
17608             node.parentNode.removeChild(node);
17609             return;
17610         }
17611         
17612         if (!node.attributes || !node.attributes.length) {
17613             this.cleanUpChildren(node);
17614             return;
17615         }
17616         
17617         function cleanAttr(n,v)
17618         {
17619             
17620             if (v.match(/^\./) || v.match(/^\//)) {
17621                 return;
17622             }
17623             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
17624                 return;
17625             }
17626             if (v.match(/^#/)) {
17627                 return;
17628             }
17629 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
17630             node.removeAttribute(n);
17631             
17632         }
17633         
17634         var cwhite = this.cwhite;
17635         var cblack = this.cblack;
17636             
17637         function cleanStyle(n,v)
17638         {
17639             if (v.match(/expression/)) { //XSS?? should we even bother..
17640                 node.removeAttribute(n);
17641                 return;
17642             }
17643             
17644             var parts = v.split(/;/);
17645             var clean = [];
17646             
17647             Roo.each(parts, function(p) {
17648                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
17649                 if (!p.length) {
17650                     return true;
17651                 }
17652                 var l = p.split(':').shift().replace(/\s+/g,'');
17653                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
17654                 
17655                 if ( cwhite.length && cblack.indexOf(l) > -1) {
17656 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17657                     //node.removeAttribute(n);
17658                     return true;
17659                 }
17660                 //Roo.log()
17661                 // only allow 'c whitelisted system attributes'
17662                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
17663 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17664                     //node.removeAttribute(n);
17665                     return true;
17666                 }
17667                 
17668                 
17669                  
17670                 
17671                 clean.push(p);
17672                 return true;
17673             });
17674             if (clean.length) { 
17675                 node.setAttribute(n, clean.join(';'));
17676             } else {
17677                 node.removeAttribute(n);
17678             }
17679             
17680         }
17681         
17682         
17683         for (var i = node.attributes.length-1; i > -1 ; i--) {
17684             var a = node.attributes[i];
17685             //console.log(a);
17686             
17687             if (a.name.toLowerCase().substr(0,2)=='on')  {
17688                 node.removeAttribute(a.name);
17689                 continue;
17690             }
17691             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
17692                 node.removeAttribute(a.name);
17693                 continue;
17694             }
17695             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
17696                 cleanAttr(a.name,a.value); // fixme..
17697                 continue;
17698             }
17699             if (a.name == 'style') {
17700                 cleanStyle(a.name,a.value);
17701                 continue;
17702             }
17703             /// clean up MS crap..
17704             // tecnically this should be a list of valid class'es..
17705             
17706             
17707             if (a.name == 'class') {
17708                 if (a.value.match(/^Mso/)) {
17709                     node.className = '';
17710                 }
17711                 
17712                 if (a.value.match(/body/)) {
17713                     node.className = '';
17714                 }
17715                 continue;
17716             }
17717             
17718             // style cleanup!?
17719             // class cleanup?
17720             
17721         }
17722         
17723         
17724         this.cleanUpChildren(node);
17725         
17726         
17727     },
17728     /**
17729      * Clean up MS wordisms...
17730      */
17731     cleanWord : function(node)
17732     {
17733         var _t = this;
17734         var cleanWordChildren = function()
17735         {
17736             if (!node.childNodes.length) {
17737                 return;
17738             }
17739             for (var i = node.childNodes.length-1; i > -1 ; i--) {
17740                _t.cleanWord(node.childNodes[i]);
17741             }
17742         }
17743         
17744         
17745         if (!node) {
17746             this.cleanWord(this.doc.body);
17747             return;
17748         }
17749         if (node.nodeName == "#text") {
17750             // clean up silly Windows -- stuff?
17751             return; 
17752         }
17753         if (node.nodeName == "#comment") {
17754             node.parentNode.removeChild(node);
17755             // clean up silly Windows -- stuff?
17756             return; 
17757         }
17758         
17759         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
17760             node.parentNode.removeChild(node);
17761             return;
17762         }
17763         
17764         // remove - but keep children..
17765         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
17766             while (node.childNodes.length) {
17767                 var cn = node.childNodes[0];
17768                 node.removeChild(cn);
17769                 node.parentNode.insertBefore(cn, node);
17770             }
17771             node.parentNode.removeChild(node);
17772             cleanWordChildren();
17773             return;
17774         }
17775         // clean styles
17776         if (node.className.length) {
17777             
17778             var cn = node.className.split(/\W+/);
17779             var cna = [];
17780             Roo.each(cn, function(cls) {
17781                 if (cls.match(/Mso[a-zA-Z]+/)) {
17782                     return;
17783                 }
17784                 cna.push(cls);
17785             });
17786             node.className = cna.length ? cna.join(' ') : '';
17787             if (!cna.length) {
17788                 node.removeAttribute("class");
17789             }
17790         }
17791         
17792         if (node.hasAttribute("lang")) {
17793             node.removeAttribute("lang");
17794         }
17795         
17796         if (node.hasAttribute("style")) {
17797             
17798             var styles = node.getAttribute("style").split(";");
17799             var nstyle = [];
17800             Roo.each(styles, function(s) {
17801                 if (!s.match(/:/)) {
17802                     return;
17803                 }
17804                 var kv = s.split(":");
17805                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
17806                     return;
17807                 }
17808                 // what ever is left... we allow.
17809                 nstyle.push(s);
17810             });
17811             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
17812             if (!nstyle.length) {
17813                 node.removeAttribute('style');
17814             }
17815         }
17816         
17817         cleanWordChildren();
17818         
17819         
17820     },
17821     domToHTML : function(currentElement, depth, nopadtext) {
17822         
17823         depth = depth || 0;
17824         nopadtext = nopadtext || false;
17825     
17826         if (!currentElement) {
17827             return this.domToHTML(this.doc.body);
17828         }
17829         
17830         //Roo.log(currentElement);
17831         var j;
17832         var allText = false;
17833         var nodeName = currentElement.nodeName;
17834         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
17835         
17836         if  (nodeName == '#text') {
17837             return currentElement.nodeValue;
17838         }
17839         
17840         
17841         var ret = '';
17842         if (nodeName != 'BODY') {
17843              
17844             var i = 0;
17845             // Prints the node tagName, such as <A>, <IMG>, etc
17846             if (tagName) {
17847                 var attr = [];
17848                 for(i = 0; i < currentElement.attributes.length;i++) {
17849                     // quoting?
17850                     var aname = currentElement.attributes.item(i).name;
17851                     if (!currentElement.attributes.item(i).value.length) {
17852                         continue;
17853                     }
17854                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
17855                 }
17856                 
17857                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
17858             } 
17859             else {
17860                 
17861                 // eack
17862             }
17863         } else {
17864             tagName = false;
17865         }
17866         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
17867             return ret;
17868         }
17869         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
17870             nopadtext = true;
17871         }
17872         
17873         
17874         // Traverse the tree
17875         i = 0;
17876         var currentElementChild = currentElement.childNodes.item(i);
17877         var allText = true;
17878         var innerHTML  = '';
17879         lastnode = '';
17880         while (currentElementChild) {
17881             // Formatting code (indent the tree so it looks nice on the screen)
17882             var nopad = nopadtext;
17883             if (lastnode == 'SPAN') {
17884                 nopad  = true;
17885             }
17886             // text
17887             if  (currentElementChild.nodeName == '#text') {
17888                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
17889                 if (!nopad && toadd.length > 80) {
17890                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
17891                 }
17892                 innerHTML  += toadd;
17893                 
17894                 i++;
17895                 currentElementChild = currentElement.childNodes.item(i);
17896                 lastNode = '';
17897                 continue;
17898             }
17899             allText = false;
17900             
17901             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
17902                 
17903             // Recursively traverse the tree structure of the child node
17904             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
17905             lastnode = currentElementChild.nodeName;
17906             i++;
17907             currentElementChild=currentElement.childNodes.item(i);
17908         }
17909         
17910         ret += innerHTML;
17911         
17912         if (!allText) {
17913                 // The remaining code is mostly for formatting the tree
17914             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
17915         }
17916         
17917         
17918         if (tagName) {
17919             ret+= "</"+tagName+">";
17920         }
17921         return ret;
17922         
17923     },
17924         
17925     applyBlacklists : function()
17926     {
17927         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
17928         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
17929         
17930         this.white = [];
17931         this.black = [];
17932         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
17933             if (b.indexOf(tag) > -1) {
17934                 return;
17935             }
17936             this.white.push(tag);
17937             
17938         }, this);
17939         
17940         Roo.each(w, function(tag) {
17941             if (b.indexOf(tag) > -1) {
17942                 return;
17943             }
17944             if (this.white.indexOf(tag) > -1) {
17945                 return;
17946             }
17947             this.white.push(tag);
17948             
17949         }, this);
17950         
17951         
17952         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
17953             if (w.indexOf(tag) > -1) {
17954                 return;
17955             }
17956             this.black.push(tag);
17957             
17958         }, this);
17959         
17960         Roo.each(b, function(tag) {
17961             if (w.indexOf(tag) > -1) {
17962                 return;
17963             }
17964             if (this.black.indexOf(tag) > -1) {
17965                 return;
17966             }
17967             this.black.push(tag);
17968             
17969         }, this);
17970         
17971         
17972         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
17973         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
17974         
17975         this.cwhite = [];
17976         this.cblack = [];
17977         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
17978             if (b.indexOf(tag) > -1) {
17979                 return;
17980             }
17981             this.cwhite.push(tag);
17982             
17983         }, this);
17984         
17985         Roo.each(w, function(tag) {
17986             if (b.indexOf(tag) > -1) {
17987                 return;
17988             }
17989             if (this.cwhite.indexOf(tag) > -1) {
17990                 return;
17991             }
17992             this.cwhite.push(tag);
17993             
17994         }, this);
17995         
17996         
17997         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
17998             if (w.indexOf(tag) > -1) {
17999                 return;
18000             }
18001             this.cblack.push(tag);
18002             
18003         }, this);
18004         
18005         Roo.each(b, function(tag) {
18006             if (w.indexOf(tag) > -1) {
18007                 return;
18008             }
18009             if (this.cblack.indexOf(tag) > -1) {
18010                 return;
18011             }
18012             this.cblack.push(tag);
18013             
18014         }, this);
18015     }
18016     
18017     // hide stuff that is not compatible
18018     /**
18019      * @event blur
18020      * @hide
18021      */
18022     /**
18023      * @event change
18024      * @hide
18025      */
18026     /**
18027      * @event focus
18028      * @hide
18029      */
18030     /**
18031      * @event specialkey
18032      * @hide
18033      */
18034     /**
18035      * @cfg {String} fieldClass @hide
18036      */
18037     /**
18038      * @cfg {String} focusClass @hide
18039      */
18040     /**
18041      * @cfg {String} autoCreate @hide
18042      */
18043     /**
18044      * @cfg {String} inputType @hide
18045      */
18046     /**
18047      * @cfg {String} invalidClass @hide
18048      */
18049     /**
18050      * @cfg {String} invalidText @hide
18051      */
18052     /**
18053      * @cfg {String} msgFx @hide
18054      */
18055     /**
18056      * @cfg {String} validateOnBlur @hide
18057      */
18058 });
18059
18060 Roo.HtmlEditorCore.white = [
18061         'area', 'br', 'img', 'input', 'hr', 'wbr',
18062         
18063        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
18064        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
18065        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
18066        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
18067        'table',   'ul',         'xmp', 
18068        
18069        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
18070       'thead',   'tr', 
18071      
18072       'dir', 'menu', 'ol', 'ul', 'dl',
18073        
18074       'embed',  'object'
18075 ];
18076
18077
18078 Roo.HtmlEditorCore.black = [
18079     //    'embed',  'object', // enable - backend responsiblity to clean thiese
18080         'applet', // 
18081         'base',   'basefont', 'bgsound', 'blink',  'body', 
18082         'frame',  'frameset', 'head',    'html',   'ilayer', 
18083         'iframe', 'layer',  'link',     'meta',    'object',   
18084         'script', 'style' ,'title',  'xml' // clean later..
18085 ];
18086 Roo.HtmlEditorCore.clean = [
18087     'script', 'style', 'title', 'xml'
18088 ];
18089 Roo.HtmlEditorCore.remove = [
18090     'font'
18091 ];
18092 // attributes..
18093
18094 Roo.HtmlEditorCore.ablack = [
18095     'on'
18096 ];
18097     
18098 Roo.HtmlEditorCore.aclean = [ 
18099     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
18100 ];
18101
18102 // protocols..
18103 Roo.HtmlEditorCore.pwhite= [
18104         'http',  'https',  'mailto'
18105 ];
18106
18107 // white listed style attributes.
18108 Roo.HtmlEditorCore.cwhite= [
18109       //  'text-align', /// default is to allow most things..
18110       
18111          
18112 //        'font-size'//??
18113 ];
18114
18115 // black listed style attributes.
18116 Roo.HtmlEditorCore.cblack= [
18117       //  'font-size' -- this can be set by the project 
18118 ];
18119
18120
18121 Roo.HtmlEditorCore.swapCodes   =[ 
18122     [    8211, "--" ], 
18123     [    8212, "--" ], 
18124     [    8216,  "'" ],  
18125     [    8217, "'" ],  
18126     [    8220, '"' ],  
18127     [    8221, '"' ],  
18128     [    8226, "*" ],  
18129     [    8230, "..." ]
18130 ]; 
18131
18132     /*
18133  * - LGPL
18134  *
18135  * HtmlEditor
18136  * 
18137  */
18138
18139 /**
18140  * @class Roo.bootstrap.HtmlEditor
18141  * @extends Roo.bootstrap.TextArea
18142  * Bootstrap HtmlEditor class
18143
18144  * @constructor
18145  * Create a new HtmlEditor
18146  * @param {Object} config The config object
18147  */
18148
18149 Roo.bootstrap.HtmlEditor = function(config){
18150     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
18151     if (!this.toolbars) {
18152         this.toolbars = [];
18153     }
18154     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
18155     this.addEvents({
18156             /**
18157              * @event initialize
18158              * Fires when the editor is fully initialized (including the iframe)
18159              * @param {HtmlEditor} this
18160              */
18161             initialize: true,
18162             /**
18163              * @event activate
18164              * Fires when the editor is first receives the focus. Any insertion must wait
18165              * until after this event.
18166              * @param {HtmlEditor} this
18167              */
18168             activate: true,
18169              /**
18170              * @event beforesync
18171              * Fires before the textarea is updated with content from the editor iframe. Return false
18172              * to cancel the sync.
18173              * @param {HtmlEditor} this
18174              * @param {String} html
18175              */
18176             beforesync: true,
18177              /**
18178              * @event beforepush
18179              * Fires before the iframe editor is updated with content from the textarea. Return false
18180              * to cancel the push.
18181              * @param {HtmlEditor} this
18182              * @param {String} html
18183              */
18184             beforepush: true,
18185              /**
18186              * @event sync
18187              * Fires when the textarea is updated with content from the editor iframe.
18188              * @param {HtmlEditor} this
18189              * @param {String} html
18190              */
18191             sync: true,
18192              /**
18193              * @event push
18194              * Fires when the iframe editor is updated with content from the textarea.
18195              * @param {HtmlEditor} this
18196              * @param {String} html
18197              */
18198             push: true,
18199              /**
18200              * @event editmodechange
18201              * Fires when the editor switches edit modes
18202              * @param {HtmlEditor} this
18203              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
18204              */
18205             editmodechange: true,
18206             /**
18207              * @event editorevent
18208              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
18209              * @param {HtmlEditor} this
18210              */
18211             editorevent: true,
18212             /**
18213              * @event firstfocus
18214              * Fires when on first focus - needed by toolbars..
18215              * @param {HtmlEditor} this
18216              */
18217             firstfocus: true,
18218             /**
18219              * @event autosave
18220              * Auto save the htmlEditor value as a file into Events
18221              * @param {HtmlEditor} this
18222              */
18223             autosave: true,
18224             /**
18225              * @event savedpreview
18226              * preview the saved version of htmlEditor
18227              * @param {HtmlEditor} this
18228              */
18229             savedpreview: true
18230         });
18231 };
18232
18233
18234 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
18235     
18236     
18237       /**
18238      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
18239      */
18240     toolbars : false,
18241    
18242      /**
18243      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
18244      *                        Roo.resizable.
18245      */
18246     resizable : false,
18247      /**
18248      * @cfg {Number} height (in pixels)
18249      */   
18250     height: 300,
18251    /**
18252      * @cfg {Number} width (in pixels)
18253      */   
18254     width: false,
18255     
18256     /**
18257      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
18258      * 
18259      */
18260     stylesheets: false,
18261     
18262     // id of frame..
18263     frameId: false,
18264     
18265     // private properties
18266     validationEvent : false,
18267     deferHeight: true,
18268     initialized : false,
18269     activated : false,
18270     
18271     onFocus : Roo.emptyFn,
18272     iframePad:3,
18273     hideMode:'offsets',
18274     
18275     
18276     tbContainer : false,
18277     
18278     toolbarContainer :function() {
18279         return this.wrap.select('.x-html-editor-tb',true).first();
18280     },
18281
18282     /**
18283      * Protected method that will not generally be called directly. It
18284      * is called when the editor creates its toolbar. Override this method if you need to
18285      * add custom toolbar buttons.
18286      * @param {HtmlEditor} editor
18287      */
18288     createToolbar : function(){
18289         
18290         Roo.log("create toolbars");
18291         
18292         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
18293         this.toolbars[0].render(this.toolbarContainer());
18294         
18295         return;
18296         
18297 //        if (!editor.toolbars || !editor.toolbars.length) {
18298 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
18299 //        }
18300 //        
18301 //        for (var i =0 ; i < editor.toolbars.length;i++) {
18302 //            editor.toolbars[i] = Roo.factory(
18303 //                    typeof(editor.toolbars[i]) == 'string' ?
18304 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
18305 //                Roo.bootstrap.HtmlEditor);
18306 //            editor.toolbars[i].init(editor);
18307 //        }
18308     },
18309
18310      
18311     // private
18312     onRender : function(ct, position)
18313     {
18314        // Roo.log("Call onRender: " + this.xtype);
18315         var _t = this;
18316         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
18317       
18318         this.wrap = this.inputEl().wrap({
18319             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
18320         });
18321         
18322         this.editorcore.onRender(ct, position);
18323          
18324         if (this.resizable) {
18325             this.resizeEl = new Roo.Resizable(this.wrap, {
18326                 pinned : true,
18327                 wrap: true,
18328                 dynamic : true,
18329                 minHeight : this.height,
18330                 height: this.height,
18331                 handles : this.resizable,
18332                 width: this.width,
18333                 listeners : {
18334                     resize : function(r, w, h) {
18335                         _t.onResize(w,h); // -something
18336                     }
18337                 }
18338             });
18339             
18340         }
18341         this.createToolbar(this);
18342        
18343         
18344         if(!this.width && this.resizable){
18345             this.setSize(this.wrap.getSize());
18346         }
18347         if (this.resizeEl) {
18348             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
18349             // should trigger onReize..
18350         }
18351         
18352     },
18353
18354     // private
18355     onResize : function(w, h)
18356     {
18357         Roo.log('resize: ' +w + ',' + h );
18358         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
18359         var ew = false;
18360         var eh = false;
18361         
18362         if(this.inputEl() ){
18363             if(typeof w == 'number'){
18364                 var aw = w - this.wrap.getFrameWidth('lr');
18365                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
18366                 ew = aw;
18367             }
18368             if(typeof h == 'number'){
18369                  var tbh = -11;  // fixme it needs to tool bar size!
18370                 for (var i =0; i < this.toolbars.length;i++) {
18371                     // fixme - ask toolbars for heights?
18372                     tbh += this.toolbars[i].el.getHeight();
18373                     //if (this.toolbars[i].footer) {
18374                     //    tbh += this.toolbars[i].footer.el.getHeight();
18375                     //}
18376                 }
18377               
18378                 
18379                 
18380                 
18381                 
18382                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
18383                 ah -= 5; // knock a few pixes off for look..
18384                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
18385                 var eh = ah;
18386             }
18387         }
18388         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
18389         this.editorcore.onResize(ew,eh);
18390         
18391     },
18392
18393     /**
18394      * Toggles the editor between standard and source edit mode.
18395      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
18396      */
18397     toggleSourceEdit : function(sourceEditMode)
18398     {
18399         this.editorcore.toggleSourceEdit(sourceEditMode);
18400         
18401         if(this.editorcore.sourceEditMode){
18402             Roo.log('editor - showing textarea');
18403             
18404 //            Roo.log('in');
18405 //            Roo.log(this.syncValue());
18406             this.syncValue();
18407             this.inputEl().removeClass(['hide', 'x-hidden']);
18408             this.inputEl().dom.removeAttribute('tabIndex');
18409             this.inputEl().focus();
18410         }else{
18411             Roo.log('editor - hiding textarea');
18412 //            Roo.log('out')
18413 //            Roo.log(this.pushValue()); 
18414             this.pushValue();
18415             
18416             this.inputEl().addClass(['hide', 'x-hidden']);
18417             this.inputEl().dom.setAttribute('tabIndex', -1);
18418             //this.deferFocus();
18419         }
18420          
18421         if(this.resizable){
18422             this.setSize(this.wrap.getSize());
18423         }
18424         
18425         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
18426     },
18427  
18428     // private (for BoxComponent)
18429     adjustSize : Roo.BoxComponent.prototype.adjustSize,
18430
18431     // private (for BoxComponent)
18432     getResizeEl : function(){
18433         return this.wrap;
18434     },
18435
18436     // private (for BoxComponent)
18437     getPositionEl : function(){
18438         return this.wrap;
18439     },
18440
18441     // private
18442     initEvents : function(){
18443         this.originalValue = this.getValue();
18444     },
18445
18446 //    /**
18447 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
18448 //     * @method
18449 //     */
18450 //    markInvalid : Roo.emptyFn,
18451 //    /**
18452 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
18453 //     * @method
18454 //     */
18455 //    clearInvalid : Roo.emptyFn,
18456
18457     setValue : function(v){
18458         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
18459         this.editorcore.pushValue();
18460     },
18461
18462      
18463     // private
18464     deferFocus : function(){
18465         this.focus.defer(10, this);
18466     },
18467
18468     // doc'ed in Field
18469     focus : function(){
18470         this.editorcore.focus();
18471         
18472     },
18473       
18474
18475     // private
18476     onDestroy : function(){
18477         
18478         
18479         
18480         if(this.rendered){
18481             
18482             for (var i =0; i < this.toolbars.length;i++) {
18483                 // fixme - ask toolbars for heights?
18484                 this.toolbars[i].onDestroy();
18485             }
18486             
18487             this.wrap.dom.innerHTML = '';
18488             this.wrap.remove();
18489         }
18490     },
18491
18492     // private
18493     onFirstFocus : function(){
18494         //Roo.log("onFirstFocus");
18495         this.editorcore.onFirstFocus();
18496          for (var i =0; i < this.toolbars.length;i++) {
18497             this.toolbars[i].onFirstFocus();
18498         }
18499         
18500     },
18501     
18502     // private
18503     syncValue : function()
18504     {   
18505         this.editorcore.syncValue();
18506     },
18507     
18508     pushValue : function()
18509     {   
18510         this.editorcore.pushValue();
18511     }
18512      
18513     
18514     // hide stuff that is not compatible
18515     /**
18516      * @event blur
18517      * @hide
18518      */
18519     /**
18520      * @event change
18521      * @hide
18522      */
18523     /**
18524      * @event focus
18525      * @hide
18526      */
18527     /**
18528      * @event specialkey
18529      * @hide
18530      */
18531     /**
18532      * @cfg {String} fieldClass @hide
18533      */
18534     /**
18535      * @cfg {String} focusClass @hide
18536      */
18537     /**
18538      * @cfg {String} autoCreate @hide
18539      */
18540     /**
18541      * @cfg {String} inputType @hide
18542      */
18543     /**
18544      * @cfg {String} invalidClass @hide
18545      */
18546     /**
18547      * @cfg {String} invalidText @hide
18548      */
18549     /**
18550      * @cfg {String} msgFx @hide
18551      */
18552     /**
18553      * @cfg {String} validateOnBlur @hide
18554      */
18555 });
18556  
18557     
18558    
18559    
18560    
18561       
18562 Roo.namespace('Roo.bootstrap.htmleditor');
18563 /**
18564  * @class Roo.bootstrap.HtmlEditorToolbar1
18565  * Basic Toolbar
18566  * 
18567  * Usage:
18568  *
18569  new Roo.bootstrap.HtmlEditor({
18570     ....
18571     toolbars : [
18572         new Roo.bootstrap.HtmlEditorToolbar1({
18573             disable : { fonts: 1 , format: 1, ..., ... , ...],
18574             btns : [ .... ]
18575         })
18576     }
18577      
18578  * 
18579  * @cfg {Object} disable List of elements to disable..
18580  * @cfg {Array} btns List of additional buttons.
18581  * 
18582  * 
18583  * NEEDS Extra CSS? 
18584  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
18585  */
18586  
18587 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
18588 {
18589     
18590     Roo.apply(this, config);
18591     
18592     // default disabled, based on 'good practice'..
18593     this.disable = this.disable || {};
18594     Roo.applyIf(this.disable, {
18595         fontSize : true,
18596         colors : true,
18597         specialElements : true
18598     });
18599     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
18600     
18601     this.editor = config.editor;
18602     this.editorcore = config.editor.editorcore;
18603     
18604     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
18605     
18606     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
18607     // dont call parent... till later.
18608 }
18609 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
18610      
18611     bar : true,
18612     
18613     editor : false,
18614     editorcore : false,
18615     
18616     
18617     formats : [
18618         "p" ,  
18619         "h1","h2","h3","h4","h5","h6", 
18620         "pre", "code", 
18621         "abbr", "acronym", "address", "cite", "samp", "var",
18622         'div','span'
18623     ],
18624     
18625     onRender : function(ct, position)
18626     {
18627        // Roo.log("Call onRender: " + this.xtype);
18628         
18629        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
18630        Roo.log(this.el);
18631        this.el.dom.style.marginBottom = '0';
18632        var _this = this;
18633        var editorcore = this.editorcore;
18634        var editor= this.editor;
18635        
18636        var children = [];
18637        var btn = function(id,cmd , toggle, handler){
18638        
18639             var  event = toggle ? 'toggle' : 'click';
18640        
18641             var a = {
18642                 size : 'sm',
18643                 xtype: 'Button',
18644                 xns: Roo.bootstrap,
18645                 glyphicon : id,
18646                 cmd : id || cmd,
18647                 enableToggle:toggle !== false,
18648                 //html : 'submit'
18649                 pressed : toggle ? false : null,
18650                 listeners : {}
18651             }
18652             a.listeners[toggle ? 'toggle' : 'click'] = function() {
18653                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
18654             }
18655             children.push(a);
18656             return a;
18657        }
18658         
18659         var style = {
18660                 xtype: 'Button',
18661                 size : 'sm',
18662                 xns: Roo.bootstrap,
18663                 glyphicon : 'font',
18664                 //html : 'submit'
18665                 menu : {
18666                     xtype: 'Menu',
18667                     xns: Roo.bootstrap,
18668                     items:  []
18669                 }
18670         };
18671         Roo.each(this.formats, function(f) {
18672             style.menu.items.push({
18673                 xtype :'MenuItem',
18674                 xns: Roo.bootstrap,
18675                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
18676                 tagname : f,
18677                 listeners : {
18678                     click : function()
18679                     {
18680                         editorcore.insertTag(this.tagname);
18681                         editor.focus();
18682                     }
18683                 }
18684                 
18685             });
18686         });
18687          children.push(style);   
18688             
18689             
18690         btn('bold',false,true);
18691         btn('italic',false,true);
18692         btn('align-left', 'justifyleft',true);
18693         btn('align-center', 'justifycenter',true);
18694         btn('align-right' , 'justifyright',true);
18695         btn('link', false, false, function(btn) {
18696             //Roo.log("create link?");
18697             var url = prompt(this.createLinkText, this.defaultLinkValue);
18698             if(url && url != 'http:/'+'/'){
18699                 this.editorcore.relayCmd('createlink', url);
18700             }
18701         }),
18702         btn('list','insertunorderedlist',true);
18703         btn('pencil', false,true, function(btn){
18704                 Roo.log(this);
18705                 
18706                 this.toggleSourceEdit(btn.pressed);
18707         });
18708         /*
18709         var cog = {
18710                 xtype: 'Button',
18711                 size : 'sm',
18712                 xns: Roo.bootstrap,
18713                 glyphicon : 'cog',
18714                 //html : 'submit'
18715                 menu : {
18716                     xtype: 'Menu',
18717                     xns: Roo.bootstrap,
18718                     items:  []
18719                 }
18720         };
18721         
18722         cog.menu.items.push({
18723             xtype :'MenuItem',
18724             xns: Roo.bootstrap,
18725             html : Clean styles,
18726             tagname : f,
18727             listeners : {
18728                 click : function()
18729                 {
18730                     editorcore.insertTag(this.tagname);
18731                     editor.focus();
18732                 }
18733             }
18734             
18735         });
18736        */
18737         
18738          
18739        this.xtype = 'NavSimplebar';
18740         
18741         for(var i=0;i< children.length;i++) {
18742             
18743             this.buttons.add(this.addxtypeChild(children[i]));
18744             
18745         }
18746         
18747         editor.on('editorevent', this.updateToolbar, this);
18748     },
18749     onBtnClick : function(id)
18750     {
18751        this.editorcore.relayCmd(id);
18752        this.editorcore.focus();
18753     },
18754     
18755     /**
18756      * Protected method that will not generally be called directly. It triggers
18757      * a toolbar update by reading the markup state of the current selection in the editor.
18758      */
18759     updateToolbar: function(){
18760
18761         if(!this.editorcore.activated){
18762             this.editor.onFirstFocus(); // is this neeed?
18763             return;
18764         }
18765
18766         var btns = this.buttons; 
18767         var doc = this.editorcore.doc;
18768         btns.get('bold').setActive(doc.queryCommandState('bold'));
18769         btns.get('italic').setActive(doc.queryCommandState('italic'));
18770         //btns.get('underline').setActive(doc.queryCommandState('underline'));
18771         
18772         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
18773         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
18774         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
18775         
18776         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
18777         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
18778          /*
18779         
18780         var ans = this.editorcore.getAllAncestors();
18781         if (this.formatCombo) {
18782             
18783             
18784             var store = this.formatCombo.store;
18785             this.formatCombo.setValue("");
18786             for (var i =0; i < ans.length;i++) {
18787                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
18788                     // select it..
18789                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
18790                     break;
18791                 }
18792             }
18793         }
18794         
18795         
18796         
18797         // hides menus... - so this cant be on a menu...
18798         Roo.bootstrap.MenuMgr.hideAll();
18799         */
18800         Roo.bootstrap.MenuMgr.hideAll();
18801         //this.editorsyncValue();
18802     },
18803     onFirstFocus: function() {
18804         this.buttons.each(function(item){
18805            item.enable();
18806         });
18807     },
18808     toggleSourceEdit : function(sourceEditMode){
18809         
18810           
18811         if(sourceEditMode){
18812             Roo.log("disabling buttons");
18813            this.buttons.each( function(item){
18814                 if(item.cmd != 'pencil'){
18815                     item.disable();
18816                 }
18817             });
18818           
18819         }else{
18820             Roo.log("enabling buttons");
18821             if(this.editorcore.initialized){
18822                 this.buttons.each( function(item){
18823                     item.enable();
18824                 });
18825             }
18826             
18827         }
18828         Roo.log("calling toggole on editor");
18829         // tell the editor that it's been pressed..
18830         this.editor.toggleSourceEdit(sourceEditMode);
18831        
18832     }
18833 });
18834
18835
18836
18837
18838
18839 /**
18840  * @class Roo.bootstrap.Table.AbstractSelectionModel
18841  * @extends Roo.util.Observable
18842  * Abstract base class for grid SelectionModels.  It provides the interface that should be
18843  * implemented by descendant classes.  This class should not be directly instantiated.
18844  * @constructor
18845  */
18846 Roo.bootstrap.Table.AbstractSelectionModel = function(){
18847     this.locked = false;
18848     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
18849 };
18850
18851
18852 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
18853     /** @ignore Called by the grid automatically. Do not call directly. */
18854     init : function(grid){
18855         this.grid = grid;
18856         this.initEvents();
18857     },
18858
18859     /**
18860      * Locks the selections.
18861      */
18862     lock : function(){
18863         this.locked = true;
18864     },
18865
18866     /**
18867      * Unlocks the selections.
18868      */
18869     unlock : function(){
18870         this.locked = false;
18871     },
18872
18873     /**
18874      * Returns true if the selections are locked.
18875      * @return {Boolean}
18876      */
18877     isLocked : function(){
18878         return this.locked;
18879     }
18880 });
18881 /**
18882  * @extends Roo.bootstrap.Table.AbstractSelectionModel
18883  * @class Roo.bootstrap.Table.RowSelectionModel
18884  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
18885  * It supports multiple selections and keyboard selection/navigation. 
18886  * @constructor
18887  * @param {Object} config
18888  */
18889
18890 Roo.bootstrap.Table.RowSelectionModel = function(config){
18891     Roo.apply(this, config);
18892     this.selections = new Roo.util.MixedCollection(false, function(o){
18893         return o.id;
18894     });
18895
18896     this.last = false;
18897     this.lastActive = false;
18898
18899     this.addEvents({
18900         /**
18901              * @event selectionchange
18902              * Fires when the selection changes
18903              * @param {SelectionModel} this
18904              */
18905             "selectionchange" : true,
18906         /**
18907              * @event afterselectionchange
18908              * Fires after the selection changes (eg. by key press or clicking)
18909              * @param {SelectionModel} this
18910              */
18911             "afterselectionchange" : true,
18912         /**
18913              * @event beforerowselect
18914              * Fires when a row is selected being selected, return false to cancel.
18915              * @param {SelectionModel} this
18916              * @param {Number} rowIndex The selected index
18917              * @param {Boolean} keepExisting False if other selections will be cleared
18918              */
18919             "beforerowselect" : true,
18920         /**
18921              * @event rowselect
18922              * Fires when a row is selected.
18923              * @param {SelectionModel} this
18924              * @param {Number} rowIndex The selected index
18925              * @param {Roo.data.Record} r The record
18926              */
18927             "rowselect" : true,
18928         /**
18929              * @event rowdeselect
18930              * Fires when a row is deselected.
18931              * @param {SelectionModel} this
18932              * @param {Number} rowIndex The selected index
18933              */
18934         "rowdeselect" : true
18935     });
18936     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
18937     this.locked = false;
18938 };
18939
18940 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
18941     /**
18942      * @cfg {Boolean} singleSelect
18943      * True to allow selection of only one row at a time (defaults to false)
18944      */
18945     singleSelect : false,
18946
18947     // private
18948     initEvents : function(){
18949
18950         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
18951             this.grid.on("mousedown", this.handleMouseDown, this);
18952         }else{ // allow click to work like normal
18953             this.grid.on("rowclick", this.handleDragableRowClick, this);
18954         }
18955
18956         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
18957             "up" : function(e){
18958                 if(!e.shiftKey){
18959                     this.selectPrevious(e.shiftKey);
18960                 }else if(this.last !== false && this.lastActive !== false){
18961                     var last = this.last;
18962                     this.selectRange(this.last,  this.lastActive-1);
18963                     this.grid.getView().focusRow(this.lastActive);
18964                     if(last !== false){
18965                         this.last = last;
18966                     }
18967                 }else{
18968                     this.selectFirstRow();
18969                 }
18970                 this.fireEvent("afterselectionchange", this);
18971             },
18972             "down" : function(e){
18973                 if(!e.shiftKey){
18974                     this.selectNext(e.shiftKey);
18975                 }else if(this.last !== false && this.lastActive !== false){
18976                     var last = this.last;
18977                     this.selectRange(this.last,  this.lastActive+1);
18978                     this.grid.getView().focusRow(this.lastActive);
18979                     if(last !== false){
18980                         this.last = last;
18981                     }
18982                 }else{
18983                     this.selectFirstRow();
18984                 }
18985                 this.fireEvent("afterselectionchange", this);
18986             },
18987             scope: this
18988         });
18989
18990         var view = this.grid.view;
18991         view.on("refresh", this.onRefresh, this);
18992         view.on("rowupdated", this.onRowUpdated, this);
18993         view.on("rowremoved", this.onRemove, this);
18994     },
18995
18996     // private
18997     onRefresh : function(){
18998         var ds = this.grid.dataSource, i, v = this.grid.view;
18999         var s = this.selections;
19000         s.each(function(r){
19001             if((i = ds.indexOfId(r.id)) != -1){
19002                 v.onRowSelect(i);
19003             }else{
19004                 s.remove(r);
19005             }
19006         });
19007     },
19008
19009     // private
19010     onRemove : function(v, index, r){
19011         this.selections.remove(r);
19012     },
19013
19014     // private
19015     onRowUpdated : function(v, index, r){
19016         if(this.isSelected(r)){
19017             v.onRowSelect(index);
19018         }
19019     },
19020
19021     /**
19022      * Select records.
19023      * @param {Array} records The records to select
19024      * @param {Boolean} keepExisting (optional) True to keep existing selections
19025      */
19026     selectRecords : function(records, keepExisting){
19027         if(!keepExisting){
19028             this.clearSelections();
19029         }
19030         var ds = this.grid.dataSource;
19031         for(var i = 0, len = records.length; i < len; i++){
19032             this.selectRow(ds.indexOf(records[i]), true);
19033         }
19034     },
19035
19036     /**
19037      * Gets the number of selected rows.
19038      * @return {Number}
19039      */
19040     getCount : function(){
19041         return this.selections.length;
19042     },
19043
19044     /**
19045      * Selects the first row in the grid.
19046      */
19047     selectFirstRow : function(){
19048         this.selectRow(0);
19049     },
19050
19051     /**
19052      * Select the last row.
19053      * @param {Boolean} keepExisting (optional) True to keep existing selections
19054      */
19055     selectLastRow : function(keepExisting){
19056         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
19057     },
19058
19059     /**
19060      * Selects the row immediately following the last selected row.
19061      * @param {Boolean} keepExisting (optional) True to keep existing selections
19062      */
19063     selectNext : function(keepExisting){
19064         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
19065             this.selectRow(this.last+1, keepExisting);
19066             this.grid.getView().focusRow(this.last);
19067         }
19068     },
19069
19070     /**
19071      * Selects the row that precedes the last selected row.
19072      * @param {Boolean} keepExisting (optional) True to keep existing selections
19073      */
19074     selectPrevious : function(keepExisting){
19075         if(this.last){
19076             this.selectRow(this.last-1, keepExisting);
19077             this.grid.getView().focusRow(this.last);
19078         }
19079     },
19080
19081     /**
19082      * Returns the selected records
19083      * @return {Array} Array of selected records
19084      */
19085     getSelections : function(){
19086         return [].concat(this.selections.items);
19087     },
19088
19089     /**
19090      * Returns the first selected record.
19091      * @return {Record}
19092      */
19093     getSelected : function(){
19094         return this.selections.itemAt(0);
19095     },
19096
19097
19098     /**
19099      * Clears all selections.
19100      */
19101     clearSelections : function(fast){
19102         if(this.locked) return;
19103         if(fast !== true){
19104             var ds = this.grid.dataSource;
19105             var s = this.selections;
19106             s.each(function(r){
19107                 this.deselectRow(ds.indexOfId(r.id));
19108             }, this);
19109             s.clear();
19110         }else{
19111             this.selections.clear();
19112         }
19113         this.last = false;
19114     },
19115
19116
19117     /**
19118      * Selects all rows.
19119      */
19120     selectAll : function(){
19121         if(this.locked) return;
19122         this.selections.clear();
19123         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
19124             this.selectRow(i, true);
19125         }
19126     },
19127
19128     /**
19129      * Returns True if there is a selection.
19130      * @return {Boolean}
19131      */
19132     hasSelection : function(){
19133         return this.selections.length > 0;
19134     },
19135
19136     /**
19137      * Returns True if the specified row is selected.
19138      * @param {Number/Record} record The record or index of the record to check
19139      * @return {Boolean}
19140      */
19141     isSelected : function(index){
19142         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
19143         return (r && this.selections.key(r.id) ? true : false);
19144     },
19145
19146     /**
19147      * Returns True if the specified record id is selected.
19148      * @param {String} id The id of record to check
19149      * @return {Boolean}
19150      */
19151     isIdSelected : function(id){
19152         return (this.selections.key(id) ? true : false);
19153     },
19154
19155     // private
19156     handleMouseDown : function(e, t){
19157         var view = this.grid.getView(), rowIndex;
19158         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
19159             return;
19160         };
19161         if(e.shiftKey && this.last !== false){
19162             var last = this.last;
19163             this.selectRange(last, rowIndex, e.ctrlKey);
19164             this.last = last; // reset the last
19165             view.focusRow(rowIndex);
19166         }else{
19167             var isSelected = this.isSelected(rowIndex);
19168             if(e.button !== 0 && isSelected){
19169                 view.focusRow(rowIndex);
19170             }else if(e.ctrlKey && isSelected){
19171                 this.deselectRow(rowIndex);
19172             }else if(!isSelected){
19173                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
19174                 view.focusRow(rowIndex);
19175             }
19176         }
19177         this.fireEvent("afterselectionchange", this);
19178     },
19179     // private
19180     handleDragableRowClick :  function(grid, rowIndex, e) 
19181     {
19182         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
19183             this.selectRow(rowIndex, false);
19184             grid.view.focusRow(rowIndex);
19185              this.fireEvent("afterselectionchange", this);
19186         }
19187     },
19188     
19189     /**
19190      * Selects multiple rows.
19191      * @param {Array} rows Array of the indexes of the row to select
19192      * @param {Boolean} keepExisting (optional) True to keep existing selections
19193      */
19194     selectRows : function(rows, keepExisting){
19195         if(!keepExisting){
19196             this.clearSelections();
19197         }
19198         for(var i = 0, len = rows.length; i < len; i++){
19199             this.selectRow(rows[i], true);
19200         }
19201     },
19202
19203     /**
19204      * Selects a range of rows. All rows in between startRow and endRow are also selected.
19205      * @param {Number} startRow The index of the first row in the range
19206      * @param {Number} endRow The index of the last row in the range
19207      * @param {Boolean} keepExisting (optional) True to retain existing selections
19208      */
19209     selectRange : function(startRow, endRow, keepExisting){
19210         if(this.locked) return;
19211         if(!keepExisting){
19212             this.clearSelections();
19213         }
19214         if(startRow <= endRow){
19215             for(var i = startRow; i <= endRow; i++){
19216                 this.selectRow(i, true);
19217             }
19218         }else{
19219             for(var i = startRow; i >= endRow; i--){
19220                 this.selectRow(i, true);
19221             }
19222         }
19223     },
19224
19225     /**
19226      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
19227      * @param {Number} startRow The index of the first row in the range
19228      * @param {Number} endRow The index of the last row in the range
19229      */
19230     deselectRange : function(startRow, endRow, preventViewNotify){
19231         if(this.locked) return;
19232         for(var i = startRow; i <= endRow; i++){
19233             this.deselectRow(i, preventViewNotify);
19234         }
19235     },
19236
19237     /**
19238      * Selects a row.
19239      * @param {Number} row The index of the row to select
19240      * @param {Boolean} keepExisting (optional) True to keep existing selections
19241      */
19242     selectRow : function(index, keepExisting, preventViewNotify){
19243         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
19244         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
19245             if(!keepExisting || this.singleSelect){
19246                 this.clearSelections();
19247             }
19248             var r = this.grid.dataSource.getAt(index);
19249             this.selections.add(r);
19250             this.last = this.lastActive = index;
19251             if(!preventViewNotify){
19252                 this.grid.getView().onRowSelect(index);
19253             }
19254             this.fireEvent("rowselect", this, index, r);
19255             this.fireEvent("selectionchange", this);
19256         }
19257     },
19258
19259     /**
19260      * Deselects a row.
19261      * @param {Number} row The index of the row to deselect
19262      */
19263     deselectRow : function(index, preventViewNotify){
19264         if(this.locked) return;
19265         if(this.last == index){
19266             this.last = false;
19267         }
19268         if(this.lastActive == index){
19269             this.lastActive = false;
19270         }
19271         var r = this.grid.dataSource.getAt(index);
19272         this.selections.remove(r);
19273         if(!preventViewNotify){
19274             this.grid.getView().onRowDeselect(index);
19275         }
19276         this.fireEvent("rowdeselect", this, index);
19277         this.fireEvent("selectionchange", this);
19278     },
19279
19280     // private
19281     restoreLast : function(){
19282         if(this._last){
19283             this.last = this._last;
19284         }
19285     },
19286
19287     // private
19288     acceptsNav : function(row, col, cm){
19289         return !cm.isHidden(col) && cm.isCellEditable(col, row);
19290     },
19291
19292     // private
19293     onEditorKey : function(field, e){
19294         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
19295         if(k == e.TAB){
19296             e.stopEvent();
19297             ed.completeEdit();
19298             if(e.shiftKey){
19299                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
19300             }else{
19301                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
19302             }
19303         }else if(k == e.ENTER && !e.ctrlKey){
19304             e.stopEvent();
19305             ed.completeEdit();
19306             if(e.shiftKey){
19307                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
19308             }else{
19309                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
19310             }
19311         }else if(k == e.ESC){
19312             ed.cancelEdit();
19313         }
19314         if(newCell){
19315             g.startEditing(newCell[0], newCell[1]);
19316         }
19317     }
19318 });/*
19319  * Based on:
19320  * Ext JS Library 1.1.1
19321  * Copyright(c) 2006-2007, Ext JS, LLC.
19322  *
19323  * Originally Released Under LGPL - original licence link has changed is not relivant.
19324  *
19325  * Fork - LGPL
19326  * <script type="text/javascript">
19327  */
19328  
19329 /**
19330  * @class Roo.bootstrap.PagingToolbar
19331  * @extends Roo.Row
19332  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
19333  * @constructor
19334  * Create a new PagingToolbar
19335  * @param {Object} config The config object
19336  */
19337 Roo.bootstrap.PagingToolbar = function(config)
19338 {
19339     // old args format still supported... - xtype is prefered..
19340         // created from xtype...
19341     var ds = config.dataSource;
19342     this.toolbarItems = [];
19343     if (config.items) {
19344         this.toolbarItems = config.items;
19345 //        config.items = [];
19346     }
19347     
19348     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
19349     this.ds = ds;
19350     this.cursor = 0;
19351     if (ds) { 
19352         this.bind(ds);
19353     }
19354     
19355     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
19356     
19357 };
19358
19359 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
19360     /**
19361      * @cfg {Roo.data.Store} dataSource
19362      * The underlying data store providing the paged data
19363      */
19364     /**
19365      * @cfg {String/HTMLElement/Element} container
19366      * container The id or element that will contain the toolbar
19367      */
19368     /**
19369      * @cfg {Boolean} displayInfo
19370      * True to display the displayMsg (defaults to false)
19371      */
19372     /**
19373      * @cfg {Number} pageSize
19374      * The number of records to display per page (defaults to 20)
19375      */
19376     pageSize: 20,
19377     /**
19378      * @cfg {String} displayMsg
19379      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
19380      */
19381     displayMsg : 'Displaying {0} - {1} of {2}',
19382     /**
19383      * @cfg {String} emptyMsg
19384      * The message to display when no records are found (defaults to "No data to display")
19385      */
19386     emptyMsg : 'No data to display',
19387     /**
19388      * Customizable piece of the default paging text (defaults to "Page")
19389      * @type String
19390      */
19391     beforePageText : "Page",
19392     /**
19393      * Customizable piece of the default paging text (defaults to "of %0")
19394      * @type String
19395      */
19396     afterPageText : "of {0}",
19397     /**
19398      * Customizable piece of the default paging text (defaults to "First Page")
19399      * @type String
19400      */
19401     firstText : "First Page",
19402     /**
19403      * Customizable piece of the default paging text (defaults to "Previous Page")
19404      * @type String
19405      */
19406     prevText : "Previous Page",
19407     /**
19408      * Customizable piece of the default paging text (defaults to "Next Page")
19409      * @type String
19410      */
19411     nextText : "Next Page",
19412     /**
19413      * Customizable piece of the default paging text (defaults to "Last Page")
19414      * @type String
19415      */
19416     lastText : "Last Page",
19417     /**
19418      * Customizable piece of the default paging text (defaults to "Refresh")
19419      * @type String
19420      */
19421     refreshText : "Refresh",
19422
19423     buttons : false,
19424     // private
19425     onRender : function(ct, position) 
19426     {
19427         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
19428         this.navgroup.parentId = this.id;
19429         this.navgroup.onRender(this.el, null);
19430         // add the buttons to the navgroup
19431         
19432         if(this.displayInfo){
19433             Roo.log(this.el.select('ul.navbar-nav',true).first());
19434             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
19435             this.displayEl = this.el.select('.x-paging-info', true).first();
19436 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
19437 //            this.displayEl = navel.el.select('span',true).first();
19438         }
19439         
19440         var _this = this;
19441         
19442         if(this.buttons){
19443             Roo.each(_this.buttons, function(e){
19444                Roo.factory(e).onRender(_this.el, null);
19445             });
19446         }
19447             
19448         Roo.each(_this.toolbarItems, function(e) {
19449             _this.navgroup.addItem(e);
19450         });
19451         
19452         this.first = this.navgroup.addItem({
19453             tooltip: this.firstText,
19454             cls: "prev",
19455             icon : 'fa fa-backward',
19456             disabled: true,
19457             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
19458         });
19459         
19460         this.prev =  this.navgroup.addItem({
19461             tooltip: this.prevText,
19462             cls: "prev",
19463             icon : 'fa fa-step-backward',
19464             disabled: true,
19465             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
19466         });
19467     //this.addSeparator();
19468         
19469         
19470         var field = this.navgroup.addItem( {
19471             tagtype : 'span',
19472             cls : 'x-paging-position',
19473             
19474             html : this.beforePageText  +
19475                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
19476                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
19477          } ); //?? escaped?
19478         
19479         this.field = field.el.select('input', true).first();
19480         this.field.on("keydown", this.onPagingKeydown, this);
19481         this.field.on("focus", function(){this.dom.select();});
19482     
19483     
19484         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
19485         //this.field.setHeight(18);
19486         //this.addSeparator();
19487         this.next = this.navgroup.addItem({
19488             tooltip: this.nextText,
19489             cls: "next",
19490             html : ' <i class="fa fa-step-forward">',
19491             disabled: true,
19492             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
19493         });
19494         this.last = this.navgroup.addItem({
19495             tooltip: this.lastText,
19496             icon : 'fa fa-forward',
19497             cls: "next",
19498             disabled: true,
19499             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
19500         });
19501     //this.addSeparator();
19502         this.loading = this.navgroup.addItem({
19503             tooltip: this.refreshText,
19504             icon: 'fa fa-refresh',
19505             
19506             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
19507         });
19508
19509     },
19510
19511     // private
19512     updateInfo : function(){
19513         if(this.displayEl){
19514             var count = this.ds.getCount();
19515             var msg = count == 0 ?
19516                 this.emptyMsg :
19517                 String.format(
19518                     this.displayMsg,
19519                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
19520                 );
19521             this.displayEl.update(msg);
19522         }
19523     },
19524
19525     // private
19526     onLoad : function(ds, r, o){
19527        this.cursor = o.params ? o.params.start : 0;
19528        var d = this.getPageData(),
19529             ap = d.activePage,
19530             ps = d.pages;
19531         
19532        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
19533        this.field.dom.value = ap;
19534        this.first.setDisabled(ap == 1);
19535        this.prev.setDisabled(ap == 1);
19536        this.next.setDisabled(ap == ps);
19537        this.last.setDisabled(ap == ps);
19538        this.loading.enable();
19539        this.updateInfo();
19540     },
19541
19542     // private
19543     getPageData : function(){
19544         var total = this.ds.getTotalCount();
19545         return {
19546             total : total,
19547             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
19548             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
19549         };
19550     },
19551
19552     // private
19553     onLoadError : function(){
19554         this.loading.enable();
19555     },
19556
19557     // private
19558     onPagingKeydown : function(e){
19559         var k = e.getKey();
19560         var d = this.getPageData();
19561         if(k == e.RETURN){
19562             var v = this.field.dom.value, pageNum;
19563             if(!v || isNaN(pageNum = parseInt(v, 10))){
19564                 this.field.dom.value = d.activePage;
19565                 return;
19566             }
19567             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
19568             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
19569             e.stopEvent();
19570         }
19571         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))
19572         {
19573           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
19574           this.field.dom.value = pageNum;
19575           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
19576           e.stopEvent();
19577         }
19578         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
19579         {
19580           var v = this.field.dom.value, pageNum; 
19581           var increment = (e.shiftKey) ? 10 : 1;
19582           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
19583             increment *= -1;
19584           if(!v || isNaN(pageNum = parseInt(v, 10))) {
19585             this.field.dom.value = d.activePage;
19586             return;
19587           }
19588           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
19589           {
19590             this.field.dom.value = parseInt(v, 10) + increment;
19591             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
19592             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
19593           }
19594           e.stopEvent();
19595         }
19596     },
19597
19598     // private
19599     beforeLoad : function(){
19600         if(this.loading){
19601             this.loading.disable();
19602         }
19603     },
19604
19605     // private
19606     onClick : function(which){
19607         var ds = this.ds;
19608         if (!ds) {
19609             return;
19610         }
19611         switch(which){
19612             case "first":
19613                 ds.load({params:{start: 0, limit: this.pageSize}});
19614             break;
19615             case "prev":
19616                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
19617             break;
19618             case "next":
19619                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
19620             break;
19621             case "last":
19622                 var total = ds.getTotalCount();
19623                 var extra = total % this.pageSize;
19624                 var lastStart = extra ? (total - extra) : total-this.pageSize;
19625                 ds.load({params:{start: lastStart, limit: this.pageSize}});
19626             break;
19627             case "refresh":
19628                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
19629             break;
19630         }
19631     },
19632
19633     /**
19634      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
19635      * @param {Roo.data.Store} store The data store to unbind
19636      */
19637     unbind : function(ds){
19638         ds.un("beforeload", this.beforeLoad, this);
19639         ds.un("load", this.onLoad, this);
19640         ds.un("loadexception", this.onLoadError, this);
19641         ds.un("remove", this.updateInfo, this);
19642         ds.un("add", this.updateInfo, this);
19643         this.ds = undefined;
19644     },
19645
19646     /**
19647      * Binds the paging toolbar to the specified {@link Roo.data.Store}
19648      * @param {Roo.data.Store} store The data store to bind
19649      */
19650     bind : function(ds){
19651         ds.on("beforeload", this.beforeLoad, this);
19652         ds.on("load", this.onLoad, this);
19653         ds.on("loadexception", this.onLoadError, this);
19654         ds.on("remove", this.updateInfo, this);
19655         ds.on("add", this.updateInfo, this);
19656         this.ds = ds;
19657     }
19658 });/*
19659  * - LGPL
19660  *
19661  * element
19662  * 
19663  */
19664
19665 /**
19666  * @class Roo.bootstrap.MessageBar
19667  * @extends Roo.bootstrap.Component
19668  * Bootstrap MessageBar class
19669  * @cfg {String} html contents of the MessageBar
19670  * @cfg {String} weight (info | success | warning | danger) default info
19671  * @cfg {String} beforeClass insert the bar before the given class
19672  * @cfg {Boolean} closable (true | false) default false
19673  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
19674  * 
19675  * @constructor
19676  * Create a new Element
19677  * @param {Object} config The config object
19678  */
19679
19680 Roo.bootstrap.MessageBar = function(config){
19681     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
19682 };
19683
19684 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
19685     
19686     html: '',
19687     weight: 'info',
19688     closable: false,
19689     fixed: false,
19690     beforeClass: 'bootstrap-sticky-wrap',
19691     
19692     getAutoCreate : function(){
19693         
19694         var cfg = {
19695             tag: 'div',
19696             cls: 'alert alert-dismissable alert-' + this.weight,
19697             cn: [
19698                 {
19699                     tag: 'span',
19700                     cls: 'message',
19701                     html: this.html || ''
19702                 }
19703             ]
19704         }
19705         
19706         if(this.fixed){
19707             cfg.cls += ' alert-messages-fixed';
19708         }
19709         
19710         if(this.closable){
19711             cfg.cn.push({
19712                 tag: 'button',
19713                 cls: 'close',
19714                 html: 'x'
19715             });
19716         }
19717         
19718         return cfg;
19719     },
19720     
19721     onRender : function(ct, position)
19722     {
19723         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19724         
19725         if(!this.el){
19726             var cfg = Roo.apply({},  this.getAutoCreate());
19727             cfg.id = Roo.id();
19728             
19729             if (this.cls) {
19730                 cfg.cls += ' ' + this.cls;
19731             }
19732             if (this.style) {
19733                 cfg.style = this.style;
19734             }
19735             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
19736             
19737             this.el.setVisibilityMode(Roo.Element.DISPLAY);
19738         }
19739         
19740         this.el.select('>button.close').on('click', this.hide, this);
19741         
19742     },
19743     
19744     show : function()
19745     {
19746         if (!this.rendered) {
19747             this.render();
19748         }
19749         
19750         this.el.show();
19751         
19752         this.fireEvent('show', this);
19753         
19754     },
19755     
19756     hide : function()
19757     {
19758         if (!this.rendered) {
19759             this.render();
19760         }
19761         
19762         this.el.hide();
19763         
19764         this.fireEvent('hide', this);
19765     },
19766     
19767     update : function()
19768     {
19769 //        var e = this.el.dom.firstChild;
19770 //        
19771 //        if(this.closable){
19772 //            e = e.nextSibling;
19773 //        }
19774 //        
19775 //        e.data = this.html || '';
19776
19777         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
19778     }
19779    
19780 });
19781
19782  
19783
19784      /*
19785  * - LGPL
19786  *
19787  * Graph
19788  * 
19789  */
19790
19791
19792 /**
19793  * @class Roo.bootstrap.Graph
19794  * @extends Roo.bootstrap.Component
19795  * Bootstrap Graph class
19796 > Prameters
19797  -sm {number} sm 4
19798  -md {number} md 5
19799  @cfg {String} graphtype  bar | vbar | pie
19800  @cfg {number} g_x coodinator | centre x (pie)
19801  @cfg {number} g_y coodinator | centre y (pie)
19802  @cfg {number} g_r radius (pie)
19803  @cfg {number} g_height height of the chart (respected by all elements in the set)
19804  @cfg {number} g_width width of the chart (respected by all elements in the set)
19805  @cfg {Object} title The title of the chart
19806     
19807  -{Array}  values
19808  -opts (object) options for the chart 
19809      o {
19810      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
19811      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
19812      o vgutter (number)
19813      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.
19814      o stacked (boolean) whether or not to tread values as in a stacked bar chart
19815      o to
19816      o stretch (boolean)
19817      o }
19818  -opts (object) options for the pie
19819      o{
19820      o cut
19821      o startAngle (number)
19822      o endAngle (number)
19823      } 
19824  *
19825  * @constructor
19826  * Create a new Input
19827  * @param {Object} config The config object
19828  */
19829
19830 Roo.bootstrap.Graph = function(config){
19831     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
19832     
19833     this.addEvents({
19834         // img events
19835         /**
19836          * @event click
19837          * The img click event for the img.
19838          * @param {Roo.EventObject} e
19839          */
19840         "click" : true
19841     });
19842 };
19843
19844 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
19845     
19846     sm: 4,
19847     md: 5,
19848     graphtype: 'bar',
19849     g_height: 250,
19850     g_width: 400,
19851     g_x: 50,
19852     g_y: 50,
19853     g_r: 30,
19854     opts:{
19855         //g_colors: this.colors,
19856         g_type: 'soft',
19857         g_gutter: '20%'
19858
19859     },
19860     title : false,
19861
19862     getAutoCreate : function(){
19863         
19864         var cfg = {
19865             tag: 'div',
19866             html : null
19867         }
19868         
19869         
19870         return  cfg;
19871     },
19872
19873     onRender : function(ct,position){
19874         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
19875         this.raphael = Raphael(this.el.dom);
19876         
19877                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19878                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19879                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19880                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
19881                 /*
19882                 r.text(160, 10, "Single Series Chart").attr(txtattr);
19883                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
19884                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
19885                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
19886                 
19887                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
19888                 r.barchart(330, 10, 300, 220, data1);
19889                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
19890                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
19891                 */
19892                 
19893                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19894                 // r.barchart(30, 30, 560, 250,  xdata, {
19895                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
19896                 //     axis : "0 0 1 1",
19897                 //     axisxlabels :  xdata
19898                 //     //yvalues : cols,
19899                    
19900                 // });
19901 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19902 //        
19903 //        this.load(null,xdata,{
19904 //                axis : "0 0 1 1",
19905 //                axisxlabels :  xdata
19906 //                });
19907
19908     },
19909
19910     load : function(graphtype,xdata,opts){
19911         this.raphael.clear();
19912         if(!graphtype) {
19913             graphtype = this.graphtype;
19914         }
19915         if(!opts){
19916             opts = this.opts;
19917         }
19918         var r = this.raphael,
19919             fin = function () {
19920                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
19921             },
19922             fout = function () {
19923                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
19924             },
19925             pfin = function() {
19926                 this.sector.stop();
19927                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
19928
19929                 if (this.label) {
19930                     this.label[0].stop();
19931                     this.label[0].attr({ r: 7.5 });
19932                     this.label[1].attr({ "font-weight": 800 });
19933                 }
19934             },
19935             pfout = function() {
19936                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
19937
19938                 if (this.label) {
19939                     this.label[0].animate({ r: 5 }, 500, "bounce");
19940                     this.label[1].attr({ "font-weight": 400 });
19941                 }
19942             };
19943
19944         switch(graphtype){
19945             case 'bar':
19946                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19947                 break;
19948             case 'hbar':
19949                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19950                 break;
19951             case 'pie':
19952 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
19953 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
19954 //            
19955                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
19956                 
19957                 break;
19958
19959         }
19960         
19961         if(this.title){
19962             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
19963         }
19964         
19965     },
19966     
19967     setTitle: function(o)
19968     {
19969         this.title = o;
19970     },
19971     
19972     initEvents: function() {
19973         
19974         if(!this.href){
19975             this.el.on('click', this.onClick, this);
19976         }
19977     },
19978     
19979     onClick : function(e)
19980     {
19981         Roo.log('img onclick');
19982         this.fireEvent('click', this, e);
19983     }
19984    
19985 });
19986
19987  
19988 /*
19989  * - LGPL
19990  *
19991  * numberBox
19992  * 
19993  */
19994 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19995
19996 /**
19997  * @class Roo.bootstrap.dash.NumberBox
19998  * @extends Roo.bootstrap.Component
19999  * Bootstrap NumberBox class
20000  * @cfg {String} headline Box headline
20001  * @cfg {String} content Box content
20002  * @cfg {String} icon Box icon
20003  * @cfg {String} footer Footer text
20004  * @cfg {String} fhref Footer href
20005  * 
20006  * @constructor
20007  * Create a new NumberBox
20008  * @param {Object} config The config object
20009  */
20010
20011
20012 Roo.bootstrap.dash.NumberBox = function(config){
20013     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
20014     
20015 };
20016
20017 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
20018     
20019     headline : '',
20020     content : '',
20021     icon : '',
20022     footer : '',
20023     fhref : '',
20024     ficon : '',
20025     
20026     getAutoCreate : function(){
20027         
20028         var cfg = {
20029             tag : 'div',
20030             cls : 'small-box ',
20031             cn : [
20032                 {
20033                     tag : 'div',
20034                     cls : 'inner',
20035                     cn :[
20036                         {
20037                             tag : 'h3',
20038                             cls : 'roo-headline',
20039                             html : this.headline
20040                         },
20041                         {
20042                             tag : 'p',
20043                             cls : 'roo-content',
20044                             html : this.content
20045                         }
20046                     ]
20047                 }
20048             ]
20049         }
20050         
20051         if(this.icon){
20052             cfg.cn.push({
20053                 tag : 'div',
20054                 cls : 'icon',
20055                 cn :[
20056                     {
20057                         tag : 'i',
20058                         cls : 'ion ' + this.icon
20059                     }
20060                 ]
20061             });
20062         }
20063         
20064         if(this.footer){
20065             var footer = {
20066                 tag : 'a',
20067                 cls : 'small-box-footer',
20068                 href : this.fhref || '#',
20069                 html : this.footer
20070             };
20071             
20072             cfg.cn.push(footer);
20073             
20074         }
20075         
20076         return  cfg;
20077     },
20078
20079     onRender : function(ct,position){
20080         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
20081
20082
20083        
20084                 
20085     },
20086
20087     setHeadline: function (value)
20088     {
20089         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
20090     },
20091     
20092     setFooter: function (value, href)
20093     {
20094         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
20095         
20096         if(href){
20097             this.el.select('a.small-box-footer',true).first().attr('href', href);
20098         }
20099         
20100     },
20101
20102     setContent: function (value)
20103     {
20104         this.el.select('.roo-content',true).first().dom.innerHTML = value;
20105     },
20106
20107     initEvents: function() 
20108     {   
20109         
20110     }
20111     
20112 });
20113
20114  
20115 /*
20116  * - LGPL
20117  *
20118  * TabBox
20119  * 
20120  */
20121 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
20122
20123 /**
20124  * @class Roo.bootstrap.dash.TabBox
20125  * @extends Roo.bootstrap.Component
20126  * Bootstrap TabBox class
20127  * @cfg {String} title Title of the TabBox
20128  * @cfg {String} icon Icon of the TabBox
20129  * @cfg {Boolean} showtabs (true|false) show the tabs default true
20130  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
20131  * 
20132  * @constructor
20133  * Create a new TabBox
20134  * @param {Object} config The config object
20135  */
20136
20137
20138 Roo.bootstrap.dash.TabBox = function(config){
20139     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
20140     this.addEvents({
20141         // raw events
20142         /**
20143          * @event addpane
20144          * When a pane is added
20145          * @param {Roo.bootstrap.dash.TabPane} pane
20146          */
20147         "addpane" : true,
20148         /**
20149          * @event activatepane
20150          * When a pane is activated
20151          * @param {Roo.bootstrap.dash.TabPane} pane
20152          */
20153         "activatepane" : true
20154         
20155          
20156     });
20157     
20158     this.panes = [];
20159 };
20160
20161 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
20162
20163     title : '',
20164     icon : false,
20165     showtabs : true,
20166     tabScrollable : false,
20167     
20168     getChildContainer : function()
20169     {
20170         return this.el.select('.tab-content', true).first();
20171     },
20172     
20173     getAutoCreate : function(){
20174         
20175         var header = {
20176             tag: 'li',
20177             cls: 'pull-left header',
20178             html: this.title,
20179             cn : []
20180         };
20181         
20182         if(this.icon){
20183             header.cn.push({
20184                 tag: 'i',
20185                 cls: 'fa ' + this.icon
20186             });
20187         }
20188         
20189         var h = {
20190             tag: 'ul',
20191             cls: 'nav nav-tabs pull-right',
20192             cn: [
20193                 header
20194             ]
20195         };
20196         
20197         if(this.tabScrollable){
20198             h = {
20199                 tag: 'div',
20200                 cls: 'tab-header',
20201                 cn: [
20202                     {
20203                         tag: 'ul',
20204                         cls: 'nav nav-tabs pull-right',
20205                         cn: [
20206                             header
20207                         ]
20208                     }
20209                 ]
20210             }
20211         }
20212         
20213         var cfg = {
20214             tag: 'div',
20215             cls: 'nav-tabs-custom',
20216             cn: [
20217                 h,
20218                 {
20219                     tag: 'div',
20220                     cls: 'tab-content no-padding',
20221                     cn: []
20222                 }
20223             ]
20224         }
20225
20226         return  cfg;
20227     },
20228     initEvents : function()
20229     {
20230         //Roo.log('add add pane handler');
20231         this.on('addpane', this.onAddPane, this);
20232     },
20233      /**
20234      * Updates the box title
20235      * @param {String} html to set the title to.
20236      */
20237     setTitle : function(value)
20238     {
20239         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
20240     },
20241     onAddPane : function(pane)
20242     {
20243         this.panes.push(pane);
20244         //Roo.log('addpane');
20245         //Roo.log(pane);
20246         // tabs are rendere left to right..
20247         if(!this.showtabs){
20248             return;
20249         }
20250         
20251         var ctr = this.el.select('.nav-tabs', true).first();
20252          
20253          
20254         var existing = ctr.select('.nav-tab',true);
20255         var qty = existing.getCount();;
20256         
20257         
20258         var tab = ctr.createChild({
20259             tag : 'li',
20260             cls : 'nav-tab' + (qty ? '' : ' active'),
20261             cn : [
20262                 {
20263                     tag : 'a',
20264                     href:'#',
20265                     html : pane.title
20266                 }
20267             ]
20268         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
20269         pane.tab = tab;
20270         
20271         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
20272         if (!qty) {
20273             pane.el.addClass('active');
20274         }
20275         
20276                 
20277     },
20278     onTabClick : function(ev,un,ob,pane)
20279     {
20280         //Roo.log('tab - prev default');
20281         ev.preventDefault();
20282         
20283         
20284         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
20285         pane.tab.addClass('active');
20286         //Roo.log(pane.title);
20287         this.getChildContainer().select('.tab-pane',true).removeClass('active');
20288         // technically we should have a deactivate event.. but maybe add later.
20289         // and it should not de-activate the selected tab...
20290         this.fireEvent('activatepane', pane);
20291         pane.el.addClass('active');
20292         pane.fireEvent('activate');
20293         
20294         
20295     },
20296     
20297     getActivePane : function()
20298     {
20299         var r = false;
20300         Roo.each(this.panes, function(p) {
20301             if(p.el.hasClass('active')){
20302                 r = p;
20303                 return false;
20304             }
20305             
20306             return;
20307         });
20308         
20309         return r;
20310     }
20311     
20312     
20313 });
20314
20315  
20316 /*
20317  * - LGPL
20318  *
20319  * Tab pane
20320  * 
20321  */
20322 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
20323 /**
20324  * @class Roo.bootstrap.TabPane
20325  * @extends Roo.bootstrap.Component
20326  * Bootstrap TabPane class
20327  * @cfg {Boolean} active (false | true) Default false
20328  * @cfg {String} title title of panel
20329
20330  * 
20331  * @constructor
20332  * Create a new TabPane
20333  * @param {Object} config The config object
20334  */
20335
20336 Roo.bootstrap.dash.TabPane = function(config){
20337     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
20338     
20339     this.addEvents({
20340         // raw events
20341         /**
20342          * @event activate
20343          * When a pane is activated
20344          * @param {Roo.bootstrap.dash.TabPane} pane
20345          */
20346         "activate" : true
20347          
20348     });
20349 };
20350
20351 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
20352     
20353     active : false,
20354     title : '',
20355     
20356     // the tabBox that this is attached to.
20357     tab : false,
20358      
20359     getAutoCreate : function() 
20360     {
20361         var cfg = {
20362             tag: 'div',
20363             cls: 'tab-pane'
20364         }
20365         
20366         if(this.active){
20367             cfg.cls += ' active';
20368         }
20369         
20370         return cfg;
20371     },
20372     initEvents  : function()
20373     {
20374         //Roo.log('trigger add pane handler');
20375         this.parent().fireEvent('addpane', this)
20376     },
20377     
20378      /**
20379      * Updates the tab title 
20380      * @param {String} html to set the title to.
20381      */
20382     setTitle: function(str)
20383     {
20384         if (!this.tab) {
20385             return;
20386         }
20387         this.title = str;
20388         this.tab.select('a', true).first().dom.innerHTML = str;
20389         
20390     }
20391     
20392     
20393     
20394 });
20395
20396  
20397
20398
20399  /*
20400  * - LGPL
20401  *
20402  * menu
20403  * 
20404  */
20405 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20406
20407 /**
20408  * @class Roo.bootstrap.menu.Menu
20409  * @extends Roo.bootstrap.Component
20410  * Bootstrap Menu class - container for Menu
20411  * @cfg {String} html Text of the menu
20412  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
20413  * @cfg {String} icon Font awesome icon
20414  * @cfg {String} pos Menu align to (top | bottom) default bottom
20415  * 
20416  * 
20417  * @constructor
20418  * Create a new Menu
20419  * @param {Object} config The config object
20420  */
20421
20422
20423 Roo.bootstrap.menu.Menu = function(config){
20424     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
20425     
20426     this.addEvents({
20427         /**
20428          * @event beforeshow
20429          * Fires before this menu is displayed
20430          * @param {Roo.bootstrap.menu.Menu} this
20431          */
20432         beforeshow : true,
20433         /**
20434          * @event beforehide
20435          * Fires before this menu is hidden
20436          * @param {Roo.bootstrap.menu.Menu} this
20437          */
20438         beforehide : true,
20439         /**
20440          * @event show
20441          * Fires after this menu is displayed
20442          * @param {Roo.bootstrap.menu.Menu} this
20443          */
20444         show : true,
20445         /**
20446          * @event hide
20447          * Fires after this menu is hidden
20448          * @param {Roo.bootstrap.menu.Menu} this
20449          */
20450         hide : true,
20451         /**
20452          * @event click
20453          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
20454          * @param {Roo.bootstrap.menu.Menu} this
20455          * @param {Roo.EventObject} e
20456          */
20457         click : true
20458     });
20459     
20460 };
20461
20462 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
20463     
20464     submenu : false,
20465     html : '',
20466     weight : 'default',
20467     icon : false,
20468     pos : 'bottom',
20469     
20470     
20471     getChildContainer : function() {
20472         if(this.isSubMenu){
20473             return this.el;
20474         }
20475         
20476         return this.el.select('ul.dropdown-menu', true).first();  
20477     },
20478     
20479     getAutoCreate : function()
20480     {
20481         var text = [
20482             {
20483                 tag : 'span',
20484                 cls : 'roo-menu-text',
20485                 html : this.html
20486             }
20487         ];
20488         
20489         if(this.icon){
20490             text.unshift({
20491                 tag : 'i',
20492                 cls : 'fa ' + this.icon
20493             })
20494         }
20495         
20496         
20497         var cfg = {
20498             tag : 'div',
20499             cls : 'btn-group',
20500             cn : [
20501                 {
20502                     tag : 'button',
20503                     cls : 'dropdown-button btn btn-' + this.weight,
20504                     cn : text
20505                 },
20506                 {
20507                     tag : 'button',
20508                     cls : 'dropdown-toggle btn btn-' + this.weight,
20509                     cn : [
20510                         {
20511                             tag : 'span',
20512                             cls : 'caret'
20513                         }
20514                     ]
20515                 },
20516                 {
20517                     tag : 'ul',
20518                     cls : 'dropdown-menu'
20519                 }
20520             ]
20521             
20522         };
20523         
20524         if(this.pos == 'top'){
20525             cfg.cls += ' dropup';
20526         }
20527         
20528         if(this.isSubMenu){
20529             cfg = {
20530                 tag : 'ul',
20531                 cls : 'dropdown-menu'
20532             }
20533         }
20534         
20535         return cfg;
20536     },
20537     
20538     onRender : function(ct, position)
20539     {
20540         this.isSubMenu = ct.hasClass('dropdown-submenu');
20541         
20542         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
20543     },
20544     
20545     initEvents : function() 
20546     {
20547         if(this.isSubMenu){
20548             return;
20549         }
20550         
20551         this.hidden = true;
20552         
20553         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
20554         this.triggerEl.on('click', this.onTriggerPress, this);
20555         
20556         this.buttonEl = this.el.select('button.dropdown-button', true).first();
20557         this.buttonEl.on('click', this.onClick, this);
20558         
20559     },
20560     
20561     list : function()
20562     {
20563         if(this.isSubMenu){
20564             return this.el;
20565         }
20566         
20567         return this.el.select('ul.dropdown-menu', true).first();
20568     },
20569     
20570     onClick : function(e)
20571     {
20572         this.fireEvent("click", this, e);
20573     },
20574     
20575     onTriggerPress  : function(e)
20576     {   
20577         if (this.isVisible()) {
20578             this.hide();
20579         } else {
20580             this.show();
20581         }
20582     },
20583     
20584     isVisible : function(){
20585         return !this.hidden;
20586     },
20587     
20588     show : function()
20589     {
20590         this.fireEvent("beforeshow", this);
20591         
20592         this.hidden = false;
20593         this.el.addClass('open');
20594         
20595         Roo.get(document).on("mouseup", this.onMouseUp, this);
20596         
20597         this.fireEvent("show", this);
20598         
20599         
20600     },
20601     
20602     hide : function()
20603     {
20604         this.fireEvent("beforehide", this);
20605         
20606         this.hidden = true;
20607         this.el.removeClass('open');
20608         
20609         Roo.get(document).un("mouseup", this.onMouseUp);
20610         
20611         this.fireEvent("hide", this);
20612     },
20613     
20614     onMouseUp : function()
20615     {
20616         this.hide();
20617     }
20618     
20619 });
20620
20621  
20622  /*
20623  * - LGPL
20624  *
20625  * menu item
20626  * 
20627  */
20628 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20629
20630 /**
20631  * @class Roo.bootstrap.menu.Item
20632  * @extends Roo.bootstrap.Component
20633  * Bootstrap MenuItem class
20634  * @cfg {Boolean} submenu (true | false) default false
20635  * @cfg {String} html text of the item
20636  * @cfg {String} href the link
20637  * @cfg {Boolean} disable (true | false) default false
20638  * @cfg {Boolean} preventDefault (true | false) default true
20639  * @cfg {String} icon Font awesome icon
20640  * @cfg {String} pos Submenu align to (left | right) default right 
20641  * 
20642  * 
20643  * @constructor
20644  * Create a new Item
20645  * @param {Object} config The config object
20646  */
20647
20648
20649 Roo.bootstrap.menu.Item = function(config){
20650     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
20651     this.addEvents({
20652         /**
20653          * @event mouseover
20654          * Fires when the mouse is hovering over this menu
20655          * @param {Roo.bootstrap.menu.Item} this
20656          * @param {Roo.EventObject} e
20657          */
20658         mouseover : true,
20659         /**
20660          * @event mouseout
20661          * Fires when the mouse exits this menu
20662          * @param {Roo.bootstrap.menu.Item} this
20663          * @param {Roo.EventObject} e
20664          */
20665         mouseout : true,
20666         // raw events
20667         /**
20668          * @event click
20669          * The raw click event for the entire grid.
20670          * @param {Roo.EventObject} e
20671          */
20672         click : true
20673     });
20674 };
20675
20676 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
20677     
20678     submenu : false,
20679     href : '',
20680     html : '',
20681     preventDefault: true,
20682     disable : false,
20683     icon : false,
20684     pos : 'right',
20685     
20686     getAutoCreate : function()
20687     {
20688         var text = [
20689             {
20690                 tag : 'span',
20691                 cls : 'roo-menu-item-text',
20692                 html : this.html
20693             }
20694         ];
20695         
20696         if(this.icon){
20697             text.unshift({
20698                 tag : 'i',
20699                 cls : 'fa ' + this.icon
20700             })
20701         }
20702         
20703         var cfg = {
20704             tag : 'li',
20705             cn : [
20706                 {
20707                     tag : 'a',
20708                     href : this.href || '#',
20709                     cn : text
20710                 }
20711             ]
20712         };
20713         
20714         if(this.disable){
20715             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
20716         }
20717         
20718         if(this.submenu){
20719             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
20720             
20721             if(this.pos == 'left'){
20722                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
20723             }
20724         }
20725         
20726         return cfg;
20727     },
20728     
20729     initEvents : function() 
20730     {
20731         this.el.on('mouseover', this.onMouseOver, this);
20732         this.el.on('mouseout', this.onMouseOut, this);
20733         
20734         this.el.select('a', true).first().on('click', this.onClick, this);
20735         
20736     },
20737     
20738     onClick : function(e)
20739     {
20740         if(this.preventDefault){
20741             e.preventDefault();
20742         }
20743         
20744         this.fireEvent("click", this, e);
20745     },
20746     
20747     onMouseOver : function(e)
20748     {
20749         if(this.submenu && this.pos == 'left'){
20750             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
20751         }
20752         
20753         this.fireEvent("mouseover", this, e);
20754     },
20755     
20756     onMouseOut : function(e)
20757     {
20758         this.fireEvent("mouseout", this, e);
20759     }
20760 });
20761
20762  
20763
20764  /*
20765  * - LGPL
20766  *
20767  * menu separator
20768  * 
20769  */
20770 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20771
20772 /**
20773  * @class Roo.bootstrap.menu.Separator
20774  * @extends Roo.bootstrap.Component
20775  * Bootstrap Separator class
20776  * 
20777  * @constructor
20778  * Create a new Separator
20779  * @param {Object} config The config object
20780  */
20781
20782
20783 Roo.bootstrap.menu.Separator = function(config){
20784     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
20785 };
20786
20787 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
20788     
20789     getAutoCreate : function(){
20790         var cfg = {
20791             tag : 'li',
20792             cls: 'divider'
20793         };
20794         
20795         return cfg;
20796     }
20797    
20798 });
20799
20800  
20801
20802  /*
20803  * - LGPL
20804  *
20805  * Tooltip
20806  * 
20807  */
20808
20809 /**
20810  * @class Roo.bootstrap.Tooltip
20811  * Bootstrap Tooltip class
20812  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
20813  * to determine which dom element triggers the tooltip.
20814  * 
20815  * It needs to add support for additional attributes like tooltip-position
20816  * 
20817  * @constructor
20818  * Create a new Toolti
20819  * @param {Object} config The config object
20820  */
20821
20822 Roo.bootstrap.Tooltip = function(config){
20823     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
20824 };
20825
20826 Roo.apply(Roo.bootstrap.Tooltip, {
20827     /**
20828      * @function init initialize tooltip monitoring.
20829      * @static
20830      */
20831     currentEl : false,
20832     currentTip : false,
20833     currentRegion : false,
20834     
20835     //  init : delay?
20836     
20837     init : function()
20838     {
20839         Roo.get(document).on('mouseover', this.enter ,this);
20840         Roo.get(document).on('mouseout', this.leave, this);
20841          
20842         
20843         this.currentTip = new Roo.bootstrap.Tooltip();
20844     },
20845     
20846     enter : function(ev)
20847     {
20848         var dom = ev.getTarget();
20849         //Roo.log(['enter',dom]);
20850         var el = Roo.fly(dom);
20851         if (this.currentEl) {
20852             //Roo.log(dom);
20853             //Roo.log(this.currentEl);
20854             //Roo.log(this.currentEl.contains(dom));
20855             if (this.currentEl == el) {
20856                 return;
20857             }
20858             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
20859                 return;
20860             }
20861
20862         }
20863         
20864         
20865         
20866         if (this.currentTip.el) {
20867             this.currentTip.el.hide(); // force hiding...
20868         }    
20869         //Roo.log(el);
20870         if (!el.attr('tooltip')) { // parents who have tip?
20871             return;
20872         }
20873         this.currentEl = el;
20874         this.currentTip.bind(el);
20875         this.currentRegion = Roo.lib.Region.getRegion(dom);
20876         this.currentTip.enter();
20877         
20878     },
20879     leave : function(ev)
20880     {
20881         var dom = ev.getTarget();
20882         //Roo.log(['leave',dom]);
20883         if (!this.currentEl) {
20884             return;
20885         }
20886         
20887         
20888         if (dom != this.currentEl.dom) {
20889             return;
20890         }
20891         var xy = ev.getXY();
20892         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
20893             return;
20894         }
20895         // only activate leave if mouse cursor is outside... bounding box..
20896         
20897         
20898         
20899         
20900         if (this.currentTip) {
20901             this.currentTip.leave();
20902         }
20903         //Roo.log('clear currentEl');
20904         this.currentEl = false;
20905         
20906         
20907     },
20908     alignment : {
20909         'left' : ['r-l', [-2,0], 'right'],
20910         'right' : ['l-r', [2,0], 'left'],
20911         'bottom' : ['t-b', [0,2], 'top'],
20912         'top' : [ 'b-t', [0,-2], 'bottom']
20913     }
20914     
20915 });
20916
20917
20918 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
20919     
20920     
20921     bindEl : false,
20922     
20923     delay : null, // can be { show : 300 , hide: 500}
20924     
20925     timeout : null,
20926     
20927     hoverState : null, //???
20928     
20929     placement : 'bottom', 
20930     
20931     getAutoCreate : function(){
20932     
20933         var cfg = {
20934            cls : 'tooltip',
20935            role : 'tooltip',
20936            cn : [
20937                 {
20938                     cls : 'tooltip-arrow'
20939                 },
20940                 {
20941                     cls : 'tooltip-inner'
20942                 }
20943            ]
20944         };
20945         
20946         return cfg;
20947     },
20948     bind : function(el)
20949     {
20950         this.bindEl = el;
20951     },
20952       
20953     
20954     enter : function () {
20955        
20956         if (this.timeout != null) {
20957             clearTimeout(this.timeout);
20958         }
20959         
20960         this.hoverState = 'in'
20961          //Roo.log("enter - show");
20962         if (!this.delay || !this.delay.show) {
20963             this.show();
20964             return;
20965         }
20966         var _t = this;
20967         this.timeout = setTimeout(function () {
20968             if (_t.hoverState == 'in') {
20969                 _t.show();
20970             }
20971         }, this.delay.show);
20972     },
20973     leave : function()
20974     {
20975         clearTimeout(this.timeout);
20976     
20977         this.hoverState = 'out'
20978          if (!this.delay || !this.delay.hide) {
20979             this.hide();
20980             return 
20981         }
20982        
20983         var _t = this;
20984         this.timeout = setTimeout(function () {
20985             //Roo.log("leave - timeout");
20986             
20987             if (_t.hoverState == 'out') {
20988                 _t.hide();
20989                 Roo.bootstrap.Tooltip.currentEl = false;
20990             }
20991         }, delay)
20992     },
20993     
20994     show : function ()
20995     {
20996         if (!this.el) {
20997             this.render(document.body);
20998         }
20999         // set content.
21000         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
21001         this.el.select('.tooltip-inner',true).first().dom.innerHTML = this.bindEl.attr('tooltip');
21002         
21003         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
21004         
21005         var placement = typeof this.placement == 'function' ?
21006             this.placement.call(this, this.el, on_el) :
21007             this.placement;
21008             
21009         var autoToken = /\s?auto?\s?/i;
21010         var autoPlace = autoToken.test(placement);
21011         if (autoPlace) {
21012             placement = placement.replace(autoToken, '') || 'top';
21013         }
21014         
21015         //this.el.detach()
21016         //this.el.setXY([0,0]);
21017         this.el.show();
21018         //this.el.dom.style.display='block';
21019         this.el.addClass(placement);
21020         
21021         //this.el.appendTo(on_el);
21022         
21023         var p = this.getPosition();
21024         var box = this.el.getBox();
21025         
21026         if (autoPlace) {
21027             // fixme..
21028         }
21029         var align = Roo.bootstrap.Tooltip.alignment[placement]
21030         this.el.alignTo(this.bindEl, align[0],align[1]);
21031         //var arrow = this.el.select('.arrow',true).first();
21032         //arrow.set(align[2], 
21033         
21034         this.el.addClass('in fade');
21035         this.hoverState = null;
21036         
21037         if (this.el.hasClass('fade')) {
21038             // fade it?
21039         }
21040         
21041     },
21042     hide : function()
21043     {
21044          
21045         if (!this.el) {
21046             return;
21047         }
21048         //this.el.setXY([0,0]);
21049         this.el.removeClass('in');
21050         //this.el.hide();
21051         
21052     }
21053     
21054 });
21055  
21056
21057  /*
21058  * - LGPL
21059  *
21060  * Location Picker
21061  * 
21062  */
21063
21064 /**
21065  * @class Roo.bootstrap.LocationPicker
21066  * @extends Roo.bootstrap.Component
21067  * Bootstrap LocationPicker class
21068  * @cfg {Number} latitude Position when init default 0
21069  * @cfg {Number} longitude Position when init default 0
21070  * @cfg {Number} zoom default 15
21071  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
21072  * @cfg {Boolean} mapTypeControl default false
21073  * @cfg {Boolean} disableDoubleClickZoom default false
21074  * @cfg {Boolean} scrollwheel default true
21075  * @cfg {Boolean} streetViewControl default false
21076  * @cfg {Number} radius default 0
21077  * @cfg {String} locationName
21078  * @cfg {Boolean} draggable default true
21079  * @cfg {Boolean} enableAutocomplete default false
21080  * @cfg {Boolean} enableReverseGeocode default true
21081  * @cfg {String} markerTitle
21082  * 
21083  * @constructor
21084  * Create a new LocationPicker
21085  * @param {Object} config The config object
21086  */
21087
21088
21089 Roo.bootstrap.LocationPicker = function(config){
21090     
21091     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
21092     
21093      this.addEvents({
21094             /**
21095              * @event initial
21096              * Fires when the picker initialized.
21097              * @param {Roo.bootstrap.LocationPicker} this
21098              * @param {Google Location} location
21099              */
21100             initial : true,
21101             /**
21102              * @event positionchanged
21103              * Fires when the picker position changed.
21104              * @param {Roo.bootstrap.LocationPicker} this
21105              * @param {Google Location} location
21106              */
21107             positionchanged : true,
21108             /**
21109              * @event resize
21110              * Fires when the map resize.
21111              * @param {Roo.bootstrap.LocationPicker} this
21112              */
21113             resize : true,
21114             /**
21115              * @event show
21116              * Fires when the map show.
21117              * @param {Roo.bootstrap.LocationPicker} this
21118              */
21119             show : true,
21120             /**
21121              * @event hide
21122              * Fires when the map hide.
21123              * @param {Roo.bootstrap.LocationPicker} this
21124              */
21125             hide : true
21126         });
21127         
21128 };
21129
21130 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
21131     
21132     gMapContext: false,
21133     
21134     latitude: 0,
21135     longitude: 0,
21136     zoom: 15,
21137     mapTypeId: false,
21138     mapTypeControl: false,
21139     disableDoubleClickZoom: false,
21140     scrollwheel: true,
21141     streetViewControl: false,
21142     radius: 0,
21143     locationName: '',
21144     draggable: true,
21145     enableAutocomplete: false,
21146     enableReverseGeocode: true,
21147     markerTitle: '',
21148     
21149     getAutoCreate: function()
21150     {
21151
21152         var cfg = {
21153             tag: 'div',
21154             cls: 'roo-location-picker'
21155         };
21156         
21157         return cfg
21158     },
21159     
21160     initEvents: function(ct, position)
21161     {   
21162         if(!this.mapTypeId){
21163             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
21164         }
21165             
21166         if(!this.el.getWidth() || this.isApplied()){
21167             return;
21168         }
21169         
21170         this.el.setVisibilityMode(Roo.Element.DISPLAY);
21171         
21172         this.initial();
21173     },
21174     
21175     initial: function()
21176     {
21177         this.gMapContext = this.GMapContext();
21178         
21179         var _this = this;
21180         
21181         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
21182             _this.setPosition(_this.gMapContext.marker.position);
21183         });
21184         
21185         this.setPosition(this.gMapContext.location);
21186         
21187         this.fireEvent('initial', this, this.gMapContext.location);
21188     },
21189     
21190     isApplied: function() 
21191     {
21192         return this.getGmapContext() == false ? false : true;
21193     },
21194     
21195     getGmapContext: function() 
21196     {
21197         return this.gMapContext
21198     },
21199     
21200     GMapContext: function() 
21201     {
21202         var _map = new google.maps.Map(this.el.dom, this);
21203         var _marker = new google.maps.Marker({
21204             position: new google.maps.LatLng(this.latitude, this.longitude),
21205             map: _map,
21206             title: this.markerTitle,
21207             draggable: this.draggable
21208         });
21209         
21210         return {
21211             map: _map,
21212             marker: _marker,
21213             circle: null,
21214             location: _marker.position,
21215             radius: this.radius,
21216             locationName: this.locationName,
21217             addressComponents: {
21218                 formatted_address: null,
21219                 addressLine1: null,
21220                 addressLine2: null,
21221                 streetName: null,
21222                 streetNumber: null,
21223                 city: null,
21224                 district: null,
21225                 state: null,
21226                 stateOrProvince: null
21227             },
21228             settings: this,
21229             domContainer: this.el.dom,
21230             geodecoder: new google.maps.Geocoder()
21231         };
21232     },
21233     
21234     drawCircle: function(center, radius, options) 
21235     {
21236         if (this.gMapContext.circle != null) {
21237             this.gMapContext.circle.setMap(null);
21238         }
21239         if (radius > 0) {
21240             radius *= 1;
21241             options = Roo.apply({}, options, {
21242                 strokeColor: "#0000FF",
21243                 strokeOpacity: .35,
21244                 strokeWeight: 2,
21245                 fillColor: "#0000FF",
21246                 fillOpacity: .2
21247             });
21248             
21249             options.map = this.gMapContext.map;
21250             options.radius = radius;
21251             options.center = center;
21252             this.gMapContext.circle = new google.maps.Circle(options);
21253             return this.gMapContext.circle;
21254         }
21255         
21256         return null;
21257     },
21258     
21259     setPosition: function(location) 
21260     {
21261         this.gMapContext.location = location;
21262         this.gMapContext.marker.setPosition(location);
21263         this.gMapContext.map.panTo(location);
21264         this.drawCircle(location, this.gMapContext.radius, {});
21265         
21266         var _this = this;
21267         
21268         if (this.gMapContext.settings.enableReverseGeocode) {
21269             this.gMapContext.geodecoder.geocode({
21270                 latLng: this.gMapContext.location
21271             }, function(results, status) {
21272                 
21273                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
21274                     _this.gMapContext.locationName = results[0].formatted_address;
21275                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
21276                     
21277                     _this.fireEvent('positionchanged', this, location);
21278                 }
21279             });
21280             
21281             return;
21282         }
21283         
21284         this.fireEvent('positionchanged', this, location);
21285     },
21286     
21287     resize: function()
21288     {
21289         google.maps.event.trigger(this.gMapContext.map, "resize");
21290         
21291         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
21292         
21293         this.fireEvent('resize', this);
21294     },
21295     
21296     setPositionByLatLng: function(latitude, longitude)
21297     {
21298         this.setPosition(new google.maps.LatLng(latitude, longitude));
21299     },
21300     
21301     getCurrentPosition: function() 
21302     {
21303         return {
21304             latitude: this.gMapContext.location.lat(),
21305             longitude: this.gMapContext.location.lng()
21306         };
21307     },
21308     
21309     getAddressName: function() 
21310     {
21311         return this.gMapContext.locationName;
21312     },
21313     
21314     getAddressComponents: function() 
21315     {
21316         return this.gMapContext.addressComponents;
21317     },
21318     
21319     address_component_from_google_geocode: function(address_components) 
21320     {
21321         var result = {};
21322         
21323         for (var i = 0; i < address_components.length; i++) {
21324             var component = address_components[i];
21325             if (component.types.indexOf("postal_code") >= 0) {
21326                 result.postalCode = component.short_name;
21327             } else if (component.types.indexOf("street_number") >= 0) {
21328                 result.streetNumber = component.short_name;
21329             } else if (component.types.indexOf("route") >= 0) {
21330                 result.streetName = component.short_name;
21331             } else if (component.types.indexOf("neighborhood") >= 0) {
21332                 result.city = component.short_name;
21333             } else if (component.types.indexOf("locality") >= 0) {
21334                 result.city = component.short_name;
21335             } else if (component.types.indexOf("sublocality") >= 0) {
21336                 result.district = component.short_name;
21337             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
21338                 result.stateOrProvince = component.short_name;
21339             } else if (component.types.indexOf("country") >= 0) {
21340                 result.country = component.short_name;
21341             }
21342         }
21343         
21344         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
21345         result.addressLine2 = "";
21346         return result;
21347     },
21348     
21349     setZoomLevel: function(zoom)
21350     {
21351         this.gMapContext.map.setZoom(zoom);
21352     },
21353     
21354     show: function()
21355     {
21356         if(!this.el){
21357             return;
21358         }
21359         
21360         this.el.show();
21361         
21362         this.resize();
21363         
21364         this.fireEvent('show', this);
21365     },
21366     
21367     hide: function()
21368     {
21369         if(!this.el){
21370             return;
21371         }
21372         
21373         this.el.hide();
21374         
21375         this.fireEvent('hide', this);
21376     }
21377     
21378 });
21379 /*
21380  * - LGPL
21381  *
21382  * Alert
21383  * 
21384  */
21385
21386 /**
21387  * @class Roo.bootstrap.Alert
21388  * @extends Roo.bootstrap.Component
21389  * Bootstrap Alert class
21390  * @cfg {String} title The title of alert
21391  * @cfg {String} html The content of alert
21392  * @cfg {String} weight (  success | info | warning | danger )
21393  * @cfg {String} faicon font-awesomeicon
21394  * 
21395  * @constructor
21396  * Create a new alert
21397  * @param {Object} config The config object
21398  */
21399
21400
21401 Roo.bootstrap.Alert = function(config){
21402     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
21403     
21404 };
21405
21406 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
21407     
21408     title: '',
21409     html: '',
21410     weight: false,
21411     faicon: false,
21412     
21413     getAutoCreate : function()
21414     {
21415         
21416         var cfg = {
21417             tag : 'div',
21418             cls : 'alert',
21419             cn : [
21420                 {
21421                     tag : 'i',
21422                     cls : 'roo-alert-icon'
21423                     
21424                 },
21425                 {
21426                     tag : 'b',
21427                     cls : 'roo-alert-title',
21428                     html : this.title
21429                 },
21430                 {
21431                     tag : 'span',
21432                     cls : 'roo-alert-text',
21433                     html : this.html
21434                 }
21435             ]
21436         };
21437         
21438         if(this.faicon){
21439             cfg.cn[0].cls += ' fa ' + this.faicon;
21440         }
21441         
21442         if(this.weight){
21443             cfg.cls += ' alert-' + this.weight;
21444         }
21445         
21446         return cfg;
21447     },
21448     
21449     initEvents: function() 
21450     {
21451         this.el.setVisibilityMode(Roo.Element.DISPLAY);
21452     },
21453     
21454     setTitle : function(str)
21455     {
21456         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
21457     },
21458     
21459     setText : function(str)
21460     {
21461         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
21462     },
21463     
21464     setWeight : function(weight)
21465     {
21466         if(this.weight){
21467             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
21468         }
21469         
21470         this.weight = weight;
21471         
21472         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
21473     },
21474     
21475     setIcon : function(icon)
21476     {
21477         if(this.faicon){
21478             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
21479         }
21480         
21481         this.faicon = icon
21482         
21483         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
21484     },
21485     
21486     hide: function() 
21487     {
21488         this.el.hide();   
21489     },
21490     
21491     show: function() 
21492     {  
21493         this.el.show();   
21494     }
21495     
21496 });
21497
21498